Merge commit '8177130a276737d0ca4001f2d425eab588fb6450' into dev-release
diff --git a/.gitignore b/.gitignore
index dbe9747..c97f9a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,8 @@
 third_party/framework.tar.gz
 third_party/gmail/*
 third_party/gmscore/*
+third_party/google/google-java-format/1.14.0
+third_party/google/google-java-format/1.14.0.tar.gz
 third_party/google-java-format
 third_party/google-java-format.tar.gz
 third_party/goyt
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 8fbb76e..088b42f 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -8,8 +8,10 @@
 
 FMT_CMD = path.join(
     'third_party',
+    'google',
     'google-java-format',
-    'google-java-format-google-java-format-1.7',
+    '1.14.0',
+    'google-java-format-1.14.0',
     'scripts',
     'google-java-format-diff.py')
 
diff --git a/build.gradle b/build.gradle
index 560c1de..bd5538a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -345,6 +345,7 @@
                 "dart-sdk",
                 "ddmlib",
                 "gradle/gradle",
+                "google/google-java-format/1.14.0",
                 "google-java-format",
                 "iosched_2019",
                 "jacoco/0.8.2",
diff --git a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
index f3a4daa..f240706 100644
--- a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
@@ -1,33 +1,28 @@
 {
-  "configuration_format_version": 3,
-  "group_id" : "com.tools.android",
-  "artifact_id" : "chm_only_desugar_jdk_libs",
-  "version": "1.0.12",
+  "identifier": "com.tools.android:chm_only_desugar_jdk_libs:1.0.12",
+  "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
+    {
+      "api_level_below_or_equal": 23,
+      "rewrite_prefix": {
+        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
+        "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom"
+      }
+    }
   ],
+  "program_flags": [],
   "library_flags": [
     {
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
         "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers",
-        "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
         "sun.misc.Desugar": "j$.sun.misc.Desugar"
       }
     }
   ],
-  "program_flags": [
-    {
-      "api_level_below_or_equal": 23,
-      "rewrite_prefix": {
-        "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap"
-      }
-    }
-  ],
   "shrinker_config": [
     "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap$TreeBin { int lockState; }",
     "-keepclassmembers class j$.util.concurrent.ConcurrentHashMap { int sizeCtl; int transferIndex; long baseCount; int cellsBusy; }",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index b075745..778274a 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -1,216 +1,11 @@
 {
-  "configuration_format_version": 3,
-  "group_id" : "com.tools.android",
-  "artifact_id" : "desugar_jdk_libs",
-  "version": "2.0.0",
+  "identifier": "com.tools.android:desugar_jdk_libs:2.0.0",
+  "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": true,
   "common_flags": [
     {
-      "api_level_below_or_equal": 25,
-      "wrapper_conversion": [
-        "java.time.Clock"
-      ]
-    },
-    {
-      "api_level_below_or_equal": 23,
-      "wrapper_conversion": [
-        "java.util.PrimitiveIterator$OfDouble",
-        "java.util.PrimitiveIterator$OfInt",
-        "java.util.PrimitiveIterator$OfLong",
-        "java.util.Spliterator",
-        "java.util.Spliterator$OfDouble",
-        "java.util.Spliterator$OfInt",
-        "java.util.Spliterator$OfLong",
-        "java.util.Spliterator$OfPrimitive",
-        "java.util.function.BiConsumer",
-        "java.util.function.BiFunction",
-        "java.util.function.BiPredicate",
-        "java.util.function.BinaryOperator",
-        "java.util.function.Consumer",
-        "java.util.function.DoubleBinaryOperator",
-        "java.util.function.DoubleConsumer",
-        "java.util.function.DoubleFunction",
-        "java.util.function.DoublePredicate",
-        "java.util.function.DoubleToIntFunction",
-        "java.util.function.DoubleToLongFunction",
-        "java.util.function.DoubleUnaryOperator",
-        "java.util.function.Function",
-        "java.util.function.IntBinaryOperator",
-        "java.util.function.IntConsumer",
-        "java.util.function.IntFunction",
-        "java.util.function.IntPredicate",
-        "java.util.function.IntToDoubleFunction",
-        "java.util.function.IntToLongFunction",
-        "java.util.function.IntUnaryOperator",
-        "java.util.function.LongBinaryOperator",
-        "java.util.function.LongConsumer",
-        "java.util.function.LongFunction",
-        "java.util.function.LongPredicate",
-        "java.util.function.LongToDoubleFunction",
-        "java.util.function.LongToIntFunction",
-        "java.util.function.LongUnaryOperator",
-        "java.util.function.ObjDoubleConsumer",
-        "java.util.function.ObjIntConsumer",
-        "java.util.function.ObjLongConsumer",
-        "java.util.function.Predicate",
-        "java.util.function.Supplier",
-        "java.util.function.ToDoubleFunction",
-        "java.util.function.ToIntFunction",
-        "java.util.function.ToLongFunction",
-        "java.util.function.UnaryOperator",
-        "java.util.stream.BaseStream",
-        "java.util.stream.Collector",
-        "java.util.stream.DoubleStream",
-        "java.util.stream.IntStream",
-        "java.util.stream.LongStream",
-        "java.util.stream.Stream"
-      ]
-    }
-  ],
-  "library_flags": [
-    {
-      "api_level_below_or_equal": 10000,
-      "rewrite_prefix": {
-        "desugar.": "j$.desugar.",
-        "libcore.": "j$.libcore.",
-        "java.net.URLDecoder": "j$.net.URLDecoder",
-        "java.net.URLEncoder": "j$.net.URLEncoder",
-        "java.lang.Desugar": "j$.lang.Desugar",
-        "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner",
-        "sun.security.action.": "j$.sun.security.action."
-      }
-    },
-    {
-      "api_level_below_or_equal": 30,
-      "rewrite_prefix": {
-        "j$.time.": "java.time.",
-        "java.time.": "j$.time.",
-        "java.util.Desugar": "j$.util.Desugar"
-      },
-      "retarget_lib_member": {
-        "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
-      },
-      "custom_conversion": {
-        "java.time.ZonedDateTime": "java.time.TimeConversions",
-        "java.time.LocalDate": "java.time.TimeConversions",
-        "java.time.Duration": "java.time.TimeConversions",
-        "java.time.ZoneId": "java.time.TimeConversions",
-        "java.time.MonthDay": "java.time.TimeConversions",
-        "java.time.Instant": "java.time.TimeConversions"
-      }
-    },
-    {
-      "api_level_below_or_equal": 29,
-      "rewrite_prefix": {
-        "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
-      }
-    },
-    {
-      "api_level_below_or_equal": 25,
-      "rewrite_prefix": {
-        "java.io.DesugarFile" : "j$.io.DesugarFile",
-        "sun.misc.Desugar": "j$.sun.misc.Desugar",
-        "jdk.internal.": "j$.jdk.internal.",
-        "java.nio.Desugar": "j$.nio.Desugar",
-        "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel",
-        "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel",
-        "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler",
-        "java.nio.channels.Desugar": "j$.nio.channels.Desugar",
-        "java.nio.file.": "j$.nio.file.",
-        "sun.nio.cs." : "j$.sun.nio.cs.",
-        "sun.nio.fs.AbstractFileSystemProvider": "j$.sun.nio.fs.AbstractFileSystemProvider",
-        "sun.nio.fs.AbstractFileTypeDetector": "j$.sun.nio.fs.AbstractFileTypeDetector",
-        "sun.nio.fs.BasicFileAttributesHolder": "j$.sun.nio.fs.BasicFileAttributesHolder",
-        "sun.nio.fs.DynamicFileAttributeView": "j$.sun.nio.fs.DynamicFileAttributeView",
-        "sun.util.PreHashedMap": "j$.sun.util.PreHashedMap"
-      },
-      "backport": {
-        "java.lang.DesugarDouble": "java.lang.Double",
-        "java.lang.DesugarInteger": "java.lang.Integer",
-        "java.lang.DesugarLong": "java.lang.Long",
-        "java.lang.DesugarMath": "java.lang.Math"
-      },
-      "retarget_lib_member": {
-        "java.io.File#toPath": "java.io.DesugarFile"
-      }
-    },
-    {
-      "api_level_below_or_equal": 23,
-      "rewrite_prefix": {
-        "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
-        "java.io.DesugarInputStream": "j$.io.DesugarInputStream",
-        "j$.util.Optional": "java.util.Optional",
-        "j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics",
-        "j$.util.IntSummaryStatistics": "java.util.IntSummaryStatistics",
-        "j$.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatistics",
-        "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
-        "java.util.stream.": "j$.util.stream.",
-        "java.util.function.": "j$.util.function.",
-        "java.util.Comparators": "j$.util.Comparators",
-        "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
-        "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
-        "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
-        "java.util.Optional": "j$.util.Optional",
-        "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
-        "java.util.SortedSet$1": "j$.util.SortedSet$1",
-        "java.util.Spliterator": "j$.util.Spliterator",
-        "java.util.StringJoiner": "j$.util.StringJoiner",
-        "java.util.Tripwire": "j$.util.Tripwire",
-        "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers",
-        "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
-        "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
-        "java.util.AbstractList": "j$.util.AbstractList",
-        "java.util.CollSer": "j$.util.CollSer",
-        "java.util.ImmutableCollections": "j$.util.ImmutableCollections",
-        "java.util.KeyValueHolder": "j$.util.KeyValueHolder"
-      },
-      "retarget_lib_member": {
-        "java.util.Arrays#stream": "java.util.DesugarArrays",
-        "java.util.Arrays#spliterator": "java.util.DesugarArrays",
-        "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet"
-      },
-      "dont_rewrite": [
-        "java.util.Iterator#remove"
-      ],
-      "emulate_interface": {
-        "java.lang.Iterable": "j$.lang.Iterable",
-        "java.util.Map$Entry": "j$.util.Map$Entry",
-        "java.util.Collection": "j$.util.Collection",
-        "java.util.Map": "j$.util.Map",
-        "java.util.Iterator": "j$.util.Iterator",
-        "java.util.Comparator": "j$.util.Comparator",
-        "java.util.List": "j$.util.List",
-        "java.util.SortedSet": "j$.util.SortedSet",
-        "java.util.Set": "j$.util.Set",
-        "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
-      },
-      "custom_conversion": {
-        "java.util.Optional": "java.util.OptionalConversions",
-        "java.util.OptionalDouble": "java.util.OptionalConversions",
-        "java.util.OptionalInt": "java.util.OptionalConversions",
-        "java.util.OptionalLong": "java.util.OptionalConversions",
-        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
-        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
-        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
-      }
-    },
-    {
-      "api_level_below_or_equal": 18,
-      "rewrite_prefix": {
-        "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
-      },
-      "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
-      }
-    }
-  ],
-  "program_flags": [
-    {
       "api_level_below_or_equal": 10000,
       "rewrite_prefix": {
         "java.net.URLDecoder": "j$.net.URLDecoder",
@@ -223,29 +18,29 @@
         "java.time.": "j$.time.",
         "java.util.Desugar": "j$.util.Desugar"
       },
-      "retarget_lib_member": {
-        "java.util.Calendar#toInstant": "java.util.DesugarCalendar",
-        "java.util.Date#from": "java.util.DesugarDate",
-        "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone#getTimeZone": "java.util.DesugarTimeZone",
-        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
+      "retarget_method": {
+        "java.time.Instant java.util.Date#toInstant()": "java.util.DesugarDate",
+        "java.time.ZoneId java.util.TimeZone#toZoneId()": "java.util.DesugarTimeZone",
+        "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
       },
       "custom_conversion": {
-        "java.time.ZonedDateTime": "java.time.TimeConversions",
-        "java.time.LocalDate": "java.time.TimeConversions",
         "java.time.Duration": "java.time.TimeConversions",
-        "java.time.ZoneId": "java.time.TimeConversions",
+        "java.time.Instant": "java.time.TimeConversions",
+        "java.time.LocalDate": "java.time.TimeConversions",
         "java.time.MonthDay": "java.time.TimeConversions",
-        "java.time.Instant": "java.time.TimeConversions"
+        "java.time.ZoneId": "java.time.TimeConversions",
+        "java.time.ZonedDateTime": "java.time.TimeConversions"
       }
     },
     {
+      "api_level_below_or_equal": 25,
+      "wrapper_conversion": [
+        "java.time.Clock"
+      ]
+    },
+    {
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
-        "java.util.stream.": "j$.util.stream.",
-        "java.util.function.": "j$.util.function.",
         "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
         "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
         "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
@@ -253,61 +48,245 @@
         "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
         "java.util.Spliterator": "j$.util.Spliterator",
         "java.util.StringJoiner": "j$.util.StringJoiner",
+        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
         "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
         "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap"
+        "java.util.function.": "j$.util.function.",
+        "java.util.stream.": "j$.util.stream."
       },
-      "retarget_lib_member": {
-        "java.util.Arrays#stream": "java.util.DesugarArrays",
-        "java.util.Arrays#spliterator": "java.util.DesugarArrays",
-        "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet",
-        "java.util.concurrent.atomic.AtomicInteger#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicLong#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicReference#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.Collections#synchronizedMap": "java.util.DesugarCollections",
-        "java.util.Collections#synchronizedSortedMap": "java.util.DesugarCollections"
-      },
-      "dont_rewrite": [
-        "java.util.Iterator#remove"
-      ],
       "emulate_interface": {
         "java.lang.Iterable": "j$.lang.Iterable",
-        "java.util.Map$Entry": "j$.util.Map$Entry",
         "java.util.Collection": "j$.util.Collection",
-        "java.util.Map": "j$.util.Map",
-        "java.util.Iterator": "j$.util.Iterator",
         "java.util.Comparator": "j$.util.Comparator",
+        "java.util.Iterator": "j$.util.Iterator",
         "java.util.List": "j$.util.List",
-        "java.util.SortedSet": "j$.util.SortedSet",
+        "java.util.Map": "j$.util.Map",
+        "java.util.Map$Entry": "j$.util.Map$Entry",
         "java.util.Set": "j$.util.Set",
+        "java.util.SortedSet": "j$.util.SortedSet",
         "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
       },
+      "dont_rewrite": [
+        "void java.util.Iterator#remove()"
+      ],
+      "retarget_method": {
+        "java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[])": "java.util.DesugarArrays",
+        "java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[], int, int)": "java.util.DesugarArrays",
+        "java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet",
+        "java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[])": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[], int, int)": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfInt java.util.Arrays#spliterator(int[])": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfInt java.util.Arrays#spliterator(int[], int, int)": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfLong java.util.Arrays#spliterator(long[])": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfLong java.util.Arrays#spliterator(long[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.DoubleStream java.util.Arrays#stream(double[])": "java.util.DesugarArrays",
+        "java.util.stream.DoubleStream java.util.Arrays#stream(double[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.IntStream java.util.Arrays#stream(int[])": "java.util.DesugarArrays",
+        "java.util.stream.IntStream java.util.Arrays#stream(int[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.LongStream java.util.Arrays#stream(long[])": "java.util.DesugarArrays",
+        "java.util.stream.LongStream java.util.Arrays#stream(long[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[])": "java.util.DesugarArrays",
+        "java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[], int, int)": "java.util.DesugarArrays"
+      },
+      "wrapper_conversion": [
+        "java.util.function.IntUnaryOperator",
+        "java.util.function.BiFunction",
+        "java.util.function.IntConsumer",
+        "java.util.function.IntBinaryOperator",
+        "java.util.function.UnaryOperator",
+        "java.util.function.DoubleConsumer",
+        "java.util.function.IntPredicate",
+        "java.util.Spliterator$OfLong",
+        "java.util.stream.Collector",
+        "java.util.function.LongPredicate",
+        "java.util.function.ToLongFunction",
+        "java.util.function.LongToDoubleFunction",
+        "java.util.PrimitiveIterator$OfInt",
+        "java.util.function.LongToIntFunction",
+        "java.util.function.Predicate",
+        "java.util.Spliterator$OfPrimitive",
+        "java.util.function.DoubleToIntFunction",
+        "java.util.function.ObjDoubleConsumer",
+        "java.util.function.BinaryOperator",
+        "java.util.stream.DoubleStream",
+        "java.util.Spliterator$OfInt",
+        "java.util.stream.Stream",
+        "java.util.function.ObjLongConsumer",
+        "java.util.function.ToDoubleFunction",
+        "java.util.Spliterator",
+        "java.util.stream.IntStream",
+        "java.util.function.LongBinaryOperator",
+        "java.util.Spliterator$OfDouble",
+        "java.util.function.DoubleFunction",
+        "java.util.function.ObjIntConsumer",
+        "java.util.function.Function",
+        "java.util.function.Supplier",
+        "java.util.function.DoubleUnaryOperator",
+        "java.util.function.BiPredicate",
+        "java.util.PrimitiveIterator$OfDouble",
+        "java.util.function.DoubleBinaryOperator",
+        "java.util.PrimitiveIterator$OfLong",
+        "java.util.function.BiConsumer",
+        "java.util.function.IntFunction",
+        "java.util.stream.LongStream",
+        "java.util.function.IntToDoubleFunction",
+        "java.util.function.LongFunction",
+        "java.util.function.ToIntFunction",
+        "java.util.function.LongConsumer",
+        "java.util.function.Consumer",
+        "java.util.function.IntToLongFunction",
+        "java.util.function.DoubleToLongFunction",
+        "java.util.function.LongUnaryOperator",
+        "java.util.stream.BaseStream",
+        "java.util.function.DoublePredicate"
+      ],
       "custom_conversion": {
+        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions",
+        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
+        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
         "java.util.Optional": "java.util.OptionalConversions",
         "java.util.OptionalDouble": "java.util.OptionalConversions",
         "java.util.OptionalInt": "java.util.OptionalConversions",
-        "java.util.OptionalLong": "java.util.OptionalConversions",
-        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
-        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
-        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
+        "java.util.OptionalLong": "java.util.OptionalConversions"
       }
     },
     {
       "api_level_below_or_equal": 18,
       "rewrite_prefix": {
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
+      }
+    }
+  ],
+  "program_flags": [
+    {
+      "api_level_below_or_equal": 30,
+      "retarget_method": {
+        "java.time.Instant java.util.Calendar#toInstant()": "java.util.DesugarCalendar",
+        "java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate",
+        "java.util.GregorianCalendar java.util.GregorianCalendar#from(java.time.ZonedDateTime)": "java.util.DesugarGregorianCalendar",
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone",
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+      }
+    },
+    {
+      "api_level_below_or_equal": 23,
+      "retarget_method": {
+        "int java.util.concurrent.atomic.AtomicInteger#accumulateAndGet(int, java.util.function.IntBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "int java.util.concurrent.atomic.AtomicInteger#getAndAccumulate(int, java.util.function.IntBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "int java.util.concurrent.atomic.AtomicInteger#getAndUpdate(java.util.function.IntUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "int java.util.concurrent.atomic.AtomicInteger#updateAndGet(java.util.function.IntUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#accumulateAndGet(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndAccumulate(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndUpdate(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#updateAndGet(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.util.Map java.util.Collections#synchronizedMap(java.util.Map)": "java.util.DesugarCollections",
+        "java.util.SortedMap java.util.Collections#synchronizedSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
+        "long java.util.concurrent.atomic.AtomicLong#accumulateAndGet(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
+        "long java.util.concurrent.atomic.AtomicLong#getAndAccumulate(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
+        "long java.util.concurrent.atomic.AtomicLong#getAndUpdate(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
+        "long java.util.concurrent.atomic.AtomicLong#updateAndGet(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong"
+      }
+    },
+    {
+      "api_level_below_or_equal": 18,
+      "retarget_method": {
+        "boolean java.lang.Character#isBmpCodePoint(int)": "j$.lang.DesugarCharacter"
+      }
+    }
+  ],
+  "library_flags": [
+    {
+      "api_level_below_or_equal": 10000,
+      "rewrite_prefix": {
+        "desugar.": "j$.desugar.",
+        "java.lang.Desugar": "j$.lang.Desugar",
+        "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner",
+        "libcore.": "j$.libcore.",
+        "sun.security.action.": "j$.sun.security.action."
+      }
+    },
+    {
+      "api_level_below_or_equal": 30,
+      "rewrite_derived_prefix": {
+        "java.time.": {
+          "j$.time.": "java.time."
+        }
+      }
+    },
+    {
+      "api_level_below_or_equal": 29,
+      "rewrite_prefix": {
+        "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
+      }
+    },
+    {
+      "api_level_below_or_equal": 25,
+      "rewrite_prefix": {
+        "java.io.DesugarFile": "j$.io.DesugarFile",
+        "java.nio.Desugar": "j$.nio.Desugar",
+        "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel",
+        "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel",
+        "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler",
+        "java.nio.channels.Desugar": "j$.nio.channels.Desugar",
+        "java.nio.file.": "j$.nio.file.",
+        "jdk.internal.": "j$.jdk.internal.",
+        "sun.misc.Desugar": "j$.sun.misc.Desugar",
+        "sun.nio.cs.": "j$.sun.nio.cs.",
+        "sun.nio.fs.AbstractFileSystemProvider": "j$.sun.nio.fs.AbstractFileSystemProvider",
+        "sun.nio.fs.AbstractFileTypeDetector": "j$.sun.nio.fs.AbstractFileTypeDetector",
+        "sun.nio.fs.BasicFileAttributesHolder": "j$.sun.nio.fs.BasicFileAttributesHolder",
+        "sun.nio.fs.DynamicFileAttributeView": "j$.sun.nio.fs.DynamicFileAttributeView",
+        "sun.util.PreHashedMap": "j$.sun.util.PreHashedMap"
       },
-      "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
+      "retarget_method": {
+        "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays",
+        "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile",
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+      },
+      "backport": {
+        "java.lang.DesugarDouble": "java.lang.Double",
+        "java.lang.DesugarInteger": "java.lang.Integer",
+        "java.lang.DesugarLong": "java.lang.Long",
+        "java.lang.DesugarMath": "java.lang.Math"
+      },
+      "amend_library_method": [
+        "private static boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)"
+      ]
+    },
+    {
+      "api_level_below_or_equal": 23,
+      "rewrite_prefix": {
+        "java.io.DesugarInputStream": "j$.io.DesugarInputStream",
+        "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
+        "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
+        "java.util.AbstractList": "j$.util.AbstractList",
+        "java.util.CollSer": "j$.util.CollSer",
+        "java.util.Comparators": "j$.util.Comparators",
+        "java.util.ImmutableCollections": "j$.util.ImmutableCollections",
+        "java.util.KeyValueHolder": "j$.util.KeyValueHolder",
+        "java.util.SortedSet$1": "j$.util.SortedSet$1",
+        "java.util.Tripwire": "j$.util.Tripwire",
+        "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers"
+      },
+      "rewrite_derived_prefix": {
+        "java.util.DoubleSummaryStatistics": {
+          "j$.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatistics"
+        },
+        "java.util.IntSummaryStatistics": {
+          "j$.util.IntSummaryStatistics": "java.util.IntSummaryStatistics"
+        },
+        "java.util.LongSummaryStatistics": {
+          "j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics"
+        },
+        "java.util.Optional": {
+          "j$.util.Optional": "java.util.Optional"
+        }
+      }
+    },
+    {
+      "api_level_below_or_equal": 18,
+      "retarget_method": {
+        "boolean java.lang.Character#isBmpCodePoint(int)": "java.lang.DesugarCharacter"
       }
     }
   ],
@@ -326,4 +305,4 @@
     "-keepattributes InnerClasses",
     "-dontwarn sun.misc.Unsafe"
   ]
-}
+}
\ No newline at end of file
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
index 1482909..faf4817 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_alternative_3.json
@@ -1,220 +1,11 @@
 {
-  "configuration_format_version": 3,
-  "group_id" : "com.tools.android",
-  "artifact_id" : "desugar_jdk_libs_alternative_3",
-  "version": "2.0.0",
+  "identifier": "com.tools.android:desugar_jdk_libs_alternative_3:2.0.0",
+  "configuration_format_version": 100,
   "required_compilation_api_level": 30,
   "synthesized_library_classes_package_prefix": "j$.",
   "support_all_callbacks_from_library": false,
   "common_flags": [
     {
-      "api_level_below_or_equal": 25,
-      "wrapper_conversion": [
-        "java.time.Clock"
-      ]
-    },
-    {
-      "api_level_below_or_equal": 23,
-      "wrapper_conversion": [
-        "java.util.PrimitiveIterator$OfDouble",
-        "java.util.PrimitiveIterator$OfInt",
-        "java.util.PrimitiveIterator$OfLong",
-        "java.util.Spliterator",
-        "java.util.Spliterator$OfDouble",
-        "java.util.Spliterator$OfInt",
-        "java.util.Spliterator$OfLong",
-        "java.util.Spliterator$OfPrimitive",
-        "java.util.function.BiConsumer",
-        "java.util.function.BiFunction",
-        "java.util.function.BiPredicate",
-        "java.util.function.BinaryOperator",
-        "java.util.function.Consumer",
-        "java.util.function.DoubleBinaryOperator",
-        "java.util.function.DoubleConsumer",
-        "java.util.function.DoubleFunction",
-        "java.util.function.DoublePredicate",
-        "java.util.function.DoubleToIntFunction",
-        "java.util.function.DoubleToLongFunction",
-        "java.util.function.DoubleUnaryOperator",
-        "java.util.function.Function",
-        "java.util.function.IntBinaryOperator",
-        "java.util.function.IntConsumer",
-        "java.util.function.IntFunction",
-        "java.util.function.IntPredicate",
-        "java.util.function.IntToDoubleFunction",
-        "java.util.function.IntToLongFunction",
-        "java.util.function.IntUnaryOperator",
-        "java.util.function.LongBinaryOperator",
-        "java.util.function.LongConsumer",
-        "java.util.function.LongFunction",
-        "java.util.function.LongPredicate",
-        "java.util.function.LongToDoubleFunction",
-        "java.util.function.LongToIntFunction",
-        "java.util.function.LongUnaryOperator",
-        "java.util.function.ObjDoubleConsumer",
-        "java.util.function.ObjIntConsumer",
-        "java.util.function.ObjLongConsumer",
-        "java.util.function.Predicate",
-        "java.util.function.Supplier",
-        "java.util.function.ToDoubleFunction",
-        "java.util.function.ToIntFunction",
-        "java.util.function.ToLongFunction",
-        "java.util.function.UnaryOperator",
-        "java.util.stream.BaseStream",
-        "java.util.stream.Collector",
-        "java.util.stream.DoubleStream",
-        "java.util.stream.IntStream",
-        "java.util.stream.LongStream",
-        "java.util.stream.Stream"
-      ]
-    }
-  ],
-  "library_flags": [
-    {
-      "api_level_below_or_equal": 10000,
-      "rewrite_prefix": {
-        "desugar.": "j$.desugar.",
-        "libcore.": "j$.libcore.",
-        "java.net.URLDecoder": "j$.net.URLDecoder",
-        "java.net.URLEncoder": "j$.net.URLEncoder",
-        "java.lang.Desugar": "j$.lang.Desugar",
-        "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner",
-        "sun.security.action.": "j$.sun.security.action."
-      }
-    },
-    {
-      "api_level_below_or_equal": 30,
-      "rewrite_prefix": {
-        "j$.time.": "java.time.",
-        "java.time.": "j$.time.",
-        "java.util.Desugar": "j$.util.Desugar"
-      },
-      "retarget_lib_member": {
-        "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
-      },
-      "custom_conversion": {
-        "java.time.ZonedDateTime": "java.time.TimeConversions",
-        "java.time.LocalDate": "java.time.TimeConversions",
-        "java.time.Duration": "java.time.TimeConversions",
-        "java.time.ZoneId": "java.time.TimeConversions",
-        "java.time.MonthDay": "java.time.TimeConversions",
-        "java.time.Instant": "java.time.TimeConversions"
-      }
-    },
-    {
-      "api_level_below_or_equal": 29,
-      "rewrite_prefix": {
-        "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
-      }
-    },
-    {
-      "api_level_below_or_equal": 30,
-      "rewrite_prefix": {
-        "java.io.DesugarFile" : "j$.io.DesugarFile",
-        "sun.misc.Desugar": "j$.sun.misc.Desugar",
-        "jdk.internal.": "j$.jdk.internal.",
-        "java.nio.Desugar": "j$.nio.Desugar",
-        "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel",
-        "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel",
-        "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler",
-        "java.nio.channels.Desugar": "j$.nio.channels.Desugar",
-        "java.nio.file.": "j$.nio.file.",
-        "sun.nio.cs." : "j$.sun.nio.cs.",
-        "sun.nio.fs.AbstractFileSystemProvider": "j$.sun.nio.fs.AbstractFileSystemProvider",
-        "sun.nio.fs.AbstractFileTypeDetector": "j$.sun.nio.fs.AbstractFileTypeDetector",
-        "sun.nio.fs.BasicFileAttributesHolder": "j$.sun.nio.fs.BasicFileAttributesHolder",
-        "sun.nio.fs.DynamicFileAttributeView": "j$.sun.nio.fs.DynamicFileAttributeView",
-        "sun.util.PreHashedMap": "j$.sun.util.PreHashedMap"
-      },
-      "backport": {
-        "java.lang.DesugarDouble": "java.lang.Double",
-        "java.lang.DesugarInteger": "java.lang.Integer",
-        "java.lang.DesugarLong": "java.lang.Long",
-        "java.lang.DesugarMath": "java.lang.Math"
-      },
-      "retarget_lib_member": {
-        "java.io.File#toPath": "java.io.DesugarFile"
-      }
-    },
-    {
-      "api_level_below_or_equal": 23,
-      "rewrite_prefix": {
-        "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
-        "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
-        "java.io.DesugarInputStream": "j$.io.DesugarInputStream",
-        "java.io.UncheckedIOException": "j$.io.UncheckedIOException",
-        "j$.util.Optional": "java.util.Optional",
-        "j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics",
-        "j$.util.IntSummaryStatistics": "java.util.IntSummaryStatistics",
-        "j$.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatistics",
-        "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
-        "java.util.stream.": "j$.util.stream.",
-        "java.util.function.": "j$.util.function.",
-        "java.util.Comparators": "j$.util.Comparators",
-        "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
-        "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
-        "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
-        "java.util.Objects": "j$.util.Objects",
-        "java.util.Optional": "j$.util.Optional",
-        "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
-        "java.util.SortedSet$1": "j$.util.SortedSet$1",
-        "java.util.Spliterator": "j$.util.Spliterator",
-        "java.util.StringJoiner": "j$.util.StringJoiner",
-        "java.util.Tripwire": "j$.util.Tripwire",
-        "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers",
-        "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
-        "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
-        "java.util.AbstractList": "j$.util.AbstractList",
-        "java.util.CollSer": "j$.util.CollSer",
-        "java.util.ImmutableCollections": "j$.util.ImmutableCollections",
-        "java.util.KeyValueHolder": "j$.util.KeyValueHolder"
-      },
-      "retarget_lib_member": {
-        "java.util.Arrays#stream": "java.util.DesugarArrays",
-        "java.util.Arrays#spliterator": "java.util.DesugarArrays",
-        "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet",
-        "java.io.BufferedReader#lines": "java.io.DesugarBufferedReader"
-      },
-      "dont_rewrite": [
-        "java.util.Iterator#remove"
-      ],
-      "emulate_interface": {
-        "java.lang.Iterable": "j$.lang.Iterable",
-        "java.util.Map$Entry": "j$.util.Map$Entry",
-        "java.util.Collection": "j$.util.Collection",
-        "java.util.Map": "j$.util.Map",
-        "java.util.Iterator": "j$.util.Iterator",
-        "java.util.Comparator": "j$.util.Comparator",
-        "java.util.List": "j$.util.List",
-        "java.util.SortedSet": "j$.util.SortedSet",
-        "java.util.Set": "j$.util.Set",
-        "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
-      },
-      "custom_conversion": {
-        "java.util.Optional": "java.util.OptionalConversions",
-        "java.util.OptionalDouble": "java.util.OptionalConversions",
-        "java.util.OptionalInt": "java.util.OptionalConversions",
-        "java.util.OptionalLong": "java.util.OptionalConversions",
-        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
-        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
-        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
-      }
-    },
-    {
-      "api_level_below_or_equal": 18,
-      "rewrite_prefix": {
-        "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
-      },
-      "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "java.lang.DesugarCharacter"
-      }
-    }
-  ],
-  "program_flags": [
-    {
       "api_level_below_or_equal": 10000,
       "rewrite_prefix": {
         "java.net.URLDecoder": "j$.net.URLDecoder",
@@ -227,29 +18,30 @@
         "java.time.": "j$.time.",
         "java.util.Desugar": "j$.util.Desugar"
       },
-      "retarget_lib_member": {
-        "java.util.Calendar#toInstant": "java.util.DesugarCalendar",
-        "java.util.Date#from": "java.util.DesugarDate",
-        "java.util.Date#toInstant": "java.util.DesugarDate",
-        "java.util.GregorianCalendar#from": "java.util.DesugarGregorianCalendar",
-        "java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar",
-        "java.util.TimeZone#getTimeZone": "java.util.DesugarTimeZone",
-        "java.util.TimeZone#toZoneId": "java.util.DesugarTimeZone"
+      "retarget_method": {
+        "java.time.Instant java.util.Date#toInstant()": "java.util.DesugarDate",
+        "java.time.ZoneId java.util.TimeZone#toZoneId()": "java.util.DesugarTimeZone",
+        "java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
       },
       "custom_conversion": {
-        "java.time.ZonedDateTime": "java.time.TimeConversions",
-        "java.time.LocalDate": "java.time.TimeConversions",
         "java.time.Duration": "java.time.TimeConversions",
-        "java.time.ZoneId": "java.time.TimeConversions",
+        "java.time.Instant": "java.time.TimeConversions",
+        "java.time.LocalDate": "java.time.TimeConversions",
         "java.time.MonthDay": "java.time.TimeConversions",
-        "java.time.Instant": "java.time.TimeConversions"
+        "java.time.ZoneId": "java.time.TimeConversions",
+        "java.time.ZonedDateTime": "java.time.TimeConversions"
       }
     },
     {
+      "api_level_below_or_equal": 25,
+      "wrapper_conversion": [
+        "java.time.Clock"
+      ]
+    },
+    {
       "api_level_below_or_equal": 23,
       "rewrite_prefix": {
-        "java.util.stream.": "j$.util.stream.",
-        "java.util.function.": "j$.util.function.",
+        "java.io.UncheckedIOException": "j$.io.UncheckedIOException",
         "java.util.DoubleSummaryStatistics": "j$.util.DoubleSummaryStatistics",
         "java.util.IntSummaryStatistics": "j$.util.IntSummaryStatistics",
         "java.util.LongSummaryStatistics": "j$.util.LongSummaryStatistics",
@@ -258,63 +50,248 @@
         "java.util.PrimitiveIterator": "j$.util.PrimitiveIterator",
         "java.util.Spliterator": "j$.util.Spliterator",
         "java.util.StringJoiner": "j$.util.StringJoiner",
+        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
         "java.util.concurrent.ThreadLocalRandom": "j$.util.concurrent.ThreadLocalRandom",
         "java.util.concurrent.atomic.DesugarAtomic": "j$.util.concurrent.atomic.DesugarAtomic",
-        "java.util.concurrent.ConcurrentHashMap": "j$.util.concurrent.ConcurrentHashMap",
-        "java.io.UncheckedIOException": "j$.io.UncheckedIOException"
+        "java.util.function.": "j$.util.function.",
+        "java.util.stream.": "j$.util.stream."
       },
-      "retarget_lib_member": {
-        "java.util.Arrays#stream": "java.util.DesugarArrays",
-        "java.util.Arrays#spliterator": "java.util.DesugarArrays",
-        "java.util.LinkedHashSet#spliterator": "java.util.DesugarLinkedHashSet",
-        "java.util.concurrent.atomic.AtomicInteger#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicInteger#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicInteger",
-        "java.util.concurrent.atomic.AtomicLong#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicLong#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicLong",
-        "java.util.concurrent.atomic.AtomicReference#getAndUpdate": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#updateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#getAndAccumulate": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.concurrent.atomic.AtomicReference#accumulateAndGet": "java.util.concurrent.atomic.DesugarAtomicReference",
-        "java.util.Collections#synchronizedMap": "java.util.DesugarCollections",
-        "java.util.Collections#synchronizedSortedMap": "java.util.DesugarCollections",
-        "java.io.BufferedReader#lines": "java.io.DesugarBufferedReader"
-      },
-      "dont_rewrite": [
-        "java.util.Iterator#remove"
-      ],
       "emulate_interface": {
         "java.lang.Iterable": "j$.lang.Iterable",
-        "java.util.Map$Entry": "j$.util.Map$Entry",
         "java.util.Collection": "j$.util.Collection",
-        "java.util.Map": "j$.util.Map",
-        "java.util.Iterator": "j$.util.Iterator",
         "java.util.Comparator": "j$.util.Comparator",
+        "java.util.Iterator": "j$.util.Iterator",
         "java.util.List": "j$.util.List",
-        "java.util.SortedSet": "j$.util.SortedSet",
+        "java.util.Map": "j$.util.Map",
+        "java.util.Map$Entry": "j$.util.Map$Entry",
         "java.util.Set": "j$.util.Set",
+        "java.util.SortedSet": "j$.util.SortedSet",
         "java.util.concurrent.ConcurrentMap": "j$.util.concurrent.ConcurrentMap"
       },
+      "dont_rewrite": [
+        "void java.util.Iterator#remove()"
+      ],
+      "retarget_method": {
+        "java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[])": "java.util.DesugarArrays",
+        "java.util.Spliterator java.util.Arrays#spliterator(java.lang.Object[], int, int)": "java.util.DesugarArrays",
+        "java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet",
+        "java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[])": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfDouble java.util.Arrays#spliterator(double[], int, int)": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfInt java.util.Arrays#spliterator(int[])": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfInt java.util.Arrays#spliterator(int[], int, int)": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfLong java.util.Arrays#spliterator(long[])": "java.util.DesugarArrays",
+        "java.util.Spliterator$OfLong java.util.Arrays#spliterator(long[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.DoubleStream java.util.Arrays#stream(double[])": "java.util.DesugarArrays",
+        "java.util.stream.DoubleStream java.util.Arrays#stream(double[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.IntStream java.util.Arrays#stream(int[])": "java.util.DesugarArrays",
+        "java.util.stream.IntStream java.util.Arrays#stream(int[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.LongStream java.util.Arrays#stream(long[])": "java.util.DesugarArrays",
+        "java.util.stream.LongStream java.util.Arrays#stream(long[], int, int)": "java.util.DesugarArrays",
+        "java.util.stream.Stream java.io.BufferedReader#lines()": "java.io.DesugarBufferedReader",
+        "java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[])": "java.util.DesugarArrays",
+        "java.util.stream.Stream java.util.Arrays#stream(java.lang.Object[], int, int)": "java.util.DesugarArrays"
+      },
+      "wrapper_conversion": [
+        "java.util.function.IntUnaryOperator",
+        "java.util.function.BiFunction",
+        "java.util.function.IntConsumer",
+        "java.util.function.IntBinaryOperator",
+        "java.util.function.UnaryOperator",
+        "java.util.function.IntPredicate",
+        "java.util.function.DoubleConsumer",
+        "java.util.Spliterator$OfLong",
+        "java.util.stream.Collector",
+        "java.util.function.LongPredicate",
+        "java.util.function.ToLongFunction",
+        "java.util.function.LongToDoubleFunction",
+        "java.util.function.LongToIntFunction",
+        "java.util.PrimitiveIterator$OfInt",
+        "java.util.function.Predicate",
+        "java.util.Spliterator$OfPrimitive",
+        "java.util.function.DoubleToIntFunction",
+        "java.util.function.ObjDoubleConsumer",
+        "java.util.function.BinaryOperator",
+        "java.util.stream.DoubleStream",
+        "java.util.Spliterator$OfInt",
+        "java.util.stream.Stream",
+        "java.util.function.ObjLongConsumer",
+        "java.util.function.ToDoubleFunction",
+        "java.util.Spliterator",
+        "java.util.stream.IntStream",
+        "java.util.function.LongBinaryOperator",
+        "java.util.function.DoubleFunction",
+        "java.util.Spliterator$OfDouble",
+        "java.util.function.ObjIntConsumer",
+        "java.util.function.Function",
+        "java.util.function.Supplier",
+        "java.util.function.DoubleUnaryOperator",
+        "java.util.function.BiPredicate",
+        "java.util.PrimitiveIterator$OfDouble",
+        "java.util.function.DoubleBinaryOperator",
+        "java.util.PrimitiveIterator$OfLong",
+        "java.util.function.BiConsumer",
+        "java.util.function.IntFunction",
+        "java.util.function.IntToDoubleFunction",
+        "java.util.stream.LongStream",
+        "java.util.function.LongFunction",
+        "java.util.function.ToIntFunction",
+        "java.util.function.LongConsumer",
+        "java.util.function.Consumer",
+        "java.util.function.IntToLongFunction",
+        "java.util.function.DoubleToLongFunction",
+        "java.util.function.LongUnaryOperator",
+        "java.util.stream.BaseStream",
+        "java.util.function.DoublePredicate"
+      ],
       "custom_conversion": {
+        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions",
+        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
+        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
         "java.util.Optional": "java.util.OptionalConversions",
         "java.util.OptionalDouble": "java.util.OptionalConversions",
         "java.util.OptionalInt": "java.util.OptionalConversions",
-        "java.util.OptionalLong": "java.util.OptionalConversions",
-        "java.util.LongSummaryStatistics": "java.util.LongSummaryStatisticsConversions",
-        "java.util.IntSummaryStatistics": "java.util.IntSummaryStatisticsConversions",
-        "java.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatisticsConversions"
+        "java.util.OptionalLong": "java.util.OptionalConversions"
       }
     },
     {
       "api_level_below_or_equal": 18,
       "rewrite_prefix": {
         "java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
+      }
+    }
+  ],
+  "program_flags": [
+    {
+      "api_level_below_or_equal": 30,
+      "retarget_method": {
+        "java.time.Instant java.util.Calendar#toInstant()": "java.util.DesugarCalendar",
+        "java.util.Date java.util.Date#from(java.time.Instant)": "java.util.DesugarDate",
+        "java.util.GregorianCalendar java.util.GregorianCalendar#from(java.time.ZonedDateTime)": "java.util.DesugarGregorianCalendar",
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.lang.String)": "java.util.DesugarTimeZone",
+        "java.util.TimeZone java.util.TimeZone#getTimeZone(java.time.ZoneId)": "java.util.DesugarTimeZone"
+      }
+    },
+    {
+      "api_level_below_or_equal": 23,
+      "retarget_method": {
+        "int java.util.concurrent.atomic.AtomicInteger#accumulateAndGet(int, java.util.function.IntBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "int java.util.concurrent.atomic.AtomicInteger#getAndAccumulate(int, java.util.function.IntBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "int java.util.concurrent.atomic.AtomicInteger#getAndUpdate(java.util.function.IntUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "int java.util.concurrent.atomic.AtomicInteger#updateAndGet(java.util.function.IntUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#accumulateAndGet(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndAccumulate(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndUpdate(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.lang.Object java.util.concurrent.atomic.AtomicReference#updateAndGet(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+        "java.util.Map java.util.Collections#synchronizedMap(java.util.Map)": "java.util.DesugarCollections",
+        "java.util.SortedMap java.util.Collections#synchronizedSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
+        "long java.util.concurrent.atomic.AtomicLong#accumulateAndGet(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
+        "long java.util.concurrent.atomic.AtomicLong#getAndAccumulate(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
+        "long java.util.concurrent.atomic.AtomicLong#getAndUpdate(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
+        "long java.util.concurrent.atomic.AtomicLong#updateAndGet(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong"
+      }
+    },
+    {
+      "api_level_below_or_equal": 18,
+      "retarget_method": {
+        "boolean java.lang.Character#isBmpCodePoint(int)": "j$.lang.DesugarCharacter"
+      }
+    }
+  ],
+  "library_flags": [
+    {
+      "api_level_below_or_equal": 10000,
+      "rewrite_prefix": {
+        "desugar.": "j$.desugar.",
+        "java.lang.Desugar": "j$.lang.Desugar",
+        "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner",
+        "libcore.": "j$.libcore.",
+        "sun.security.action.": "j$.sun.security.action."
+      }
+    },
+    {
+      "api_level_below_or_equal": 30,
+      "rewrite_prefix": {
+        "java.io.DesugarFile": "j$.io.DesugarFile",
+        "java.nio.Desugar": "j$.nio.Desugar",
+        "java.nio.channels.AsynchronousChannel": "j$.nio.channels.AsynchronousChannel",
+        "java.nio.channels.AsynchronousFileChannel": "j$.nio.channels.AsynchronousFileChannel",
+        "java.nio.channels.CompletionHandler": "j$.nio.channels.CompletionHandler",
+        "java.nio.channels.Desugar": "j$.nio.channels.Desugar",
+        "java.nio.file.": "j$.nio.file.",
+        "jdk.internal.": "j$.jdk.internal.",
+        "sun.misc.Desugar": "j$.sun.misc.Desugar",
+        "sun.nio.cs.": "j$.sun.nio.cs.",
+        "sun.nio.fs.AbstractFileSystemProvider": "j$.sun.nio.fs.AbstractFileSystemProvider",
+        "sun.nio.fs.AbstractFileTypeDetector": "j$.sun.nio.fs.AbstractFileTypeDetector",
+        "sun.nio.fs.BasicFileAttributesHolder": "j$.sun.nio.fs.BasicFileAttributesHolder",
+        "sun.nio.fs.DynamicFileAttributeView": "j$.sun.nio.fs.DynamicFileAttributeView",
+        "sun.util.PreHashedMap": "j$.sun.util.PreHashedMap"
       },
-      "retarget_lib_member": {
-        "java.lang.Character#isBmpCodePoint": "j$.lang.DesugarCharacter"
+      "rewrite_derived_prefix": {
+        "java.time.": {
+          "j$.time.": "java.time."
+        }
+      },
+      "retarget_method": {
+        "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
+      },
+      "backport": {
+        "java.lang.DesugarDouble": "java.lang.Double",
+        "java.lang.DesugarInteger": "java.lang.Integer",
+        "java.lang.DesugarLong": "java.lang.Long",
+        "java.lang.DesugarMath": "java.lang.Math"
+      }
+    },
+    {
+      "api_level_below_or_equal": 29,
+      "rewrite_prefix": {
+        "java.util.concurrent.Flow": "j$.util.concurrent.Flow"
+      }
+    },
+    {
+      "api_level_below_or_equal": 25,
+      "retarget_method": {
+        "boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)": "java.util.DesugarArrays"
+      },
+      "amend_library_method": [
+        "private static boolean java.util.Arrays#deepEquals0(java.lang.Object, java.lang.Object)"
+      ]
+    },
+    {
+      "api_level_below_or_equal": 23,
+      "rewrite_prefix": {
+        "java.io.DesugarBufferedReader": "j$.io.DesugarBufferedReader",
+        "java.io.DesugarInputStream": "j$.io.DesugarInputStream",
+        "java.lang.FunctionalInterface": "j$.lang.FunctionalInterface",
+        "java.nio.channels.SeekableByteChannel": "j$.nio.channels.SeekableByteChannel",
+        "java.util.AbstractList": "j$.util.AbstractList",
+        "java.util.CollSer": "j$.util.CollSer",
+        "java.util.Comparators": "j$.util.Comparators",
+        "java.util.ImmutableCollections": "j$.util.ImmutableCollections",
+        "java.util.KeyValueHolder": "j$.util.KeyValueHolder",
+        "java.util.SortedSet$1": "j$.util.SortedSet$1",
+        "java.util.Tripwire": "j$.util.Tripwire",
+        "java.util.concurrent.Helpers": "j$.util.concurrent.Helpers"
+      },
+      "rewrite_derived_prefix": {
+        "java.util.DoubleSummaryStatistics": {
+          "j$.util.DoubleSummaryStatistics": "java.util.DoubleSummaryStatistics"
+        },
+        "java.util.IntSummaryStatistics": {
+          "j$.util.IntSummaryStatistics": "java.util.IntSummaryStatistics"
+        },
+        "java.util.LongSummaryStatistics": {
+          "j$.util.LongSummaryStatistics": "java.util.LongSummaryStatistics"
+        },
+        "java.util.Optional": {
+          "j$.util.Optional": "java.util.Optional"
+        }
+      }
+    },
+    {
+      "api_level_below_or_equal": 18,
+      "retarget_method": {
+        "boolean java.lang.Character#isBmpCodePoint(int)": "java.lang.DesugarCharacter"
       }
     }
   ],
@@ -332,4 +309,4 @@
     "-keepattributes InnerClasses",
     "-dontwarn sun.misc.Unsafe"
   ]
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index 7ef1569..1ae2311 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -4,8 +4,9 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -41,7 +42,7 @@
   private final boolean printVersion;
   private final Reporter reporter;
   private final int minApiLevel;
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
+  private final DesugaredLibrarySpecification desugaredLibrarySpecification;
   private final AndroidApp app;
   private final StringConsumer backportedMethodListConsumer;
   private final DexItemFactory factory;
@@ -62,7 +63,7 @@
     return minApiLevel;
   }
 
-  public LegacyDesugaredLibrarySpecification getDesugaredLibraryConfiguration() {
+  public DesugaredLibrarySpecification getDesugaredLibraryConfiguration() {
     return desugaredLibrarySpecification;
   }
 
@@ -88,7 +89,7 @@
   private BackportedMethodListCommand(
       Reporter reporter,
       int minApiLevel,
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification,
+      DesugaredLibrarySpecification desugaredLibrarySpecification,
       AndroidApp app,
       StringConsumer backportedMethodListConsumer,
       DexItemFactory factory) {
@@ -245,7 +246,7 @@
       return this;
     }
 
-    LegacyDesugaredLibrarySpecification getDesugaredLibraryConfiguration(DexItemFactory factory) {
+    DesugaredLibrarySpecification getDesugaredLibraryConfiguration(DexItemFactory factory) {
       if (desugaredLibrarySpecificationResources.isEmpty()) {
         return LegacyDesugaredLibrarySpecification.empty();
       }
@@ -254,9 +255,8 @@
       }
       StringResource desugaredLibrarySpecificationResource =
           desugaredLibrarySpecificationResources.get(0);
-      LegacyDesugaredLibrarySpecificationParser libraryParser =
-          new LegacyDesugaredLibrarySpecificationParser(factory, null, false, getMinApiLevel());
-      return libraryParser.parse(desugaredLibrarySpecificationResource);
+      return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+          desugaredLibrarySpecificationResource, factory, null, false, getMinApiLevel());
     }
 
     /** Output file for the backported method list */
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 18b7c02..a474a31 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -7,8 +7,9 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.inspector.Inspector;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -573,7 +574,7 @@
       return self();
     }
 
-    LegacyDesugaredLibrarySpecification getDesugaredLibraryConfiguration(
+    DesugaredLibrarySpecification getDesugaredLibraryConfiguration(
         DexItemFactory factory, boolean libraryCompilation) {
       if (desugaredLibrarySpecificationResources.isEmpty()) {
         return LegacyDesugaredLibrarySpecification.empty();
@@ -583,10 +584,12 @@
       }
       StringResource desugaredLibrarySpecificationResource =
           desugaredLibrarySpecificationResources.get(0);
-      LegacyDesugaredLibrarySpecificationParser libraryParser =
-          new LegacyDesugaredLibrarySpecificationParser(
-              factory, getReporter(), libraryCompilation, getMinApiLevel());
-      return libraryParser.parse(desugaredLibrarySpecificationResource);
+      return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+          desugaredLibrarySpecificationResource,
+          factory,
+          getReporter(),
+          libraryCompilation,
+          getMinApiLevel());
     }
 
     boolean hasDesugaredLibraryConfiguration() {
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 10c5c5f..851b677 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.TypeRewriter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
 import com.android.tools.r8.ir.optimize.AssertionsRewriter;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.jar.CfApplicationWriter;
@@ -195,6 +196,7 @@
       assert forTesting(options, () -> !options.testing.testEnableTestAssertions);
 
       AppView<AppInfo> appView = readApp(inputApp, options, executor, timing);
+      DesugaredLibraryAmender.run(appView);
       SyntheticItems.collectSyntheticInputs(appView);
 
       final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
@@ -210,7 +212,7 @@
             clazz -> {
               ProgramMethod classInitializer = clazz.getProgramClassInitializer();
               if (classInitializer != null) {
-                analysis.processNewlyLiveMethod(classInitializer, clazz, null);
+                analysis.processNewlyLiveMethod(classInitializer, clazz, null, null);
               }
             },
             executor);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 0967f55..c8923f8 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.inspector.Inspector;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
@@ -288,7 +288,7 @@
       intermediate |= getProgramConsumer() instanceof DexFilePerClassFileConsumer;
 
       DexItemFactory factory = new DexItemFactory();
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
+      DesugaredLibrarySpecification desugaredLibrarySpecification =
           getDesugaredLibraryConfiguration(factory, false);
 
       ImmutableList<ProguardConfigurationRule> mainDexKeepRules =
@@ -328,7 +328,7 @@
   private final boolean intermediate;
   private final DesugarGraphConsumer desugarGraphConsumer;
   private final StringConsumer desugaredLibraryKeepRuleConsumer;
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
+  private final DesugaredLibrarySpecification desugaredLibrarySpecification;
   private final String synthesizedClassPrefix;
   private final boolean skipDump;
   private final boolean enableMainDexListCheck;
@@ -390,7 +390,7 @@
       BiPredicate<String, Long> dexClassChecksumFilter,
       DesugarGraphConsumer desugarGraphConsumer,
       StringConsumer desugaredLibraryKeepRuleConsumer,
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification,
+      DesugaredLibrarySpecification desugaredLibrarySpecification,
       List<AssertionsConfiguration> assertionsConfiguration,
       List<Consumer<Inspector>> outputInspections,
       String synthesizedClassPrefix,
diff --git a/src/main/java/com/android/tools/r8/DumpOptions.java b/src/main/java/com/android/tools/r8/DumpOptions.java
index c7f3345..2726086 100644
--- a/src/main/java/com/android/tools/r8/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/DumpOptions.java
@@ -7,7 +7,7 @@
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.experimental.startup.StartupConfiguration;
 import com.android.tools.r8.features.FeatureSplitConfiguration;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
@@ -50,7 +50,7 @@
   private final Optional<Boolean> forceProguardCompatibility;
 
   // Dump if present.
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
+  private final DesugaredLibrarySpecification desugaredLibrarySpecification;
   private final FeatureSplitConfiguration featureSplitConfiguration;
   private final ProguardConfiguration proguardConfiguration;
   private final List<ProguardConfigurationRule> mainDexKeepRules;
@@ -62,7 +62,7 @@
       Tool tool,
       CompilationMode compilationMode,
       int minAPI,
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification,
+      DesugaredLibrarySpecification desugaredLibrarySpecification,
       boolean optimizeMultidexForLinearAlloc,
       int threadCount,
       DesugarState desugarState,
@@ -133,8 +133,7 @@
   }
 
   private boolean hasDesugaredLibraryConfiguration() {
-    return desugaredLibrarySpecification != null
-        && !desugaredLibrarySpecification.isEmptyConfiguration();
+    return desugaredLibrarySpecification != null && !desugaredLibrarySpecification.isEmpty();
   }
 
   public String getDesugaredLibraryJsonSource() {
@@ -186,7 +185,7 @@
     private Optional<Boolean> minification = Optional.empty();
     private Optional<Boolean> forceProguardCompatibility = Optional.empty();
     // Dump if present.
-    private LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
+    private DesugaredLibrarySpecification desugaredLibrarySpecification;
     private FeatureSplitConfiguration featureSplitConfiguration;
     private ProguardConfiguration proguardConfiguration;
     private List<ProguardConfigurationRule> mainDexKeepRules;
@@ -209,7 +208,7 @@
     }
 
     public Builder setDesugaredLibraryConfiguration(
-        LegacyDesugaredLibrarySpecification desugaredLibrarySpecification) {
+        DesugaredLibrarySpecification desugaredLibrarySpecification) {
       this.desugaredLibrarySpecification = desugaredLibrarySpecification;
       return this;
     }
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 4c49b47..bc6cb77 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -34,8 +34,9 @@
 import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
@@ -73,7 +74,7 @@
   private final Reporter reporter = new Reporter();
   private final InternalOptions options = new InternalOptions(factory, reporter);
 
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
+  private final MachineDesugaredLibrarySpecification desugaredLibrarySpecification;
   private final Path desugaredLibraryImplementation;
   private final Path outputDirectory;
 
@@ -82,8 +83,12 @@
   public GenerateLintFiles(
       String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
       throws Exception {
-    this.desugaredLibrarySpecification =
+    DesugaredLibrarySpecification specification =
         readDesugaredLibraryConfiguration(desugarConfigurationPath);
+    Path androidJarPath = getAndroidJarPath(specification.getRequiredCompilationApiLevel());
+    this.desugaredLibrarySpecification =
+        specification.toMachineSpecification(options, androidJarPath);
+
     this.desugaredLibraryImplementation = Paths.get(desugarImplementationPath);
     this.outputDirectory = Paths.get(outputDirectory);
     if (!Files.isDirectory(this.outputDirectory)) {
@@ -119,11 +124,14 @@
     return Paths.get(jar);
   }
 
-  private LegacyDesugaredLibrarySpecification readDesugaredLibraryConfiguration(
+  private DesugaredLibrarySpecification readDesugaredLibraryConfiguration(
       String desugarConfigurationPath) {
-    return new LegacyDesugaredLibrarySpecificationParser(
-            factory, reporter, false, AndroidApiLevel.B.getLevel())
-        .parse(StringResource.fromFile(Paths.get(desugarConfigurationPath)));
+    return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+        StringResource.fromFile(Paths.get(desugarConfigurationPath)),
+        factory,
+        reporter,
+        false,
+        AndroidApiLevel.B.getLevel());
   }
 
   private CfCode buildEmptyThrowingCfCode(DexMethod method) {
@@ -192,6 +200,7 @@
   }
 
   public static class SupportedMethods {
+
     public final Set<DexClass> classesWithAllMethodsSupported;
     public final Map<DexClass, List<DexEncodedMethod>> supportedMethods;
 
@@ -222,54 +231,34 @@
     Set<DexClass> classesWithAllMethodsSupported = Sets.newIdentityHashSet();
     Map<DexClass, List<DexEncodedMethod>> supportedMethods = new LinkedHashMap<>();
     for (DexProgramClass clazz : dexApplication.classes()) {
-      String className = clazz.toSourceString();
-      // All the methods with the rewritten prefix are supported.
-      for (String prefix : desugaredLibrarySpecification.getRewritePrefix().keySet()) {
-        if (clazz.accessFlags.isPublic() && className.startsWith(prefix)) {
-          DexProgramClass implementationClass =
-              implementationApplication.programDefinitionFor(clazz.getType());
-          if (implementationClass == null) {
-            throw new Exception("Implementation class not found for " + clazz.toSourceString());
+      if (clazz.accessFlags.isPublic()
+          && desugaredLibrarySpecification.getRewriteType().containsKey(clazz.type)) {
+        DexProgramClass implementationClass =
+            implementationApplication.programDefinitionFor(clazz.getType());
+        if (implementationClass == null) {
+          throw new Exception("Implementation class not found for " + clazz.toSourceString());
+        }
+        boolean allMethodsAdded = true;
+        for (DexEncodedMethod method : clazz.methods()) {
+          if (!method.isPublic()) {
+            continue;
           }
-          boolean allMethodsAddad = true;
-          for (DexEncodedMethod method : clazz.methods()) {
-            if (!method.isPublic()) {
-              continue;
-            }
-            ProgramMethod implementationMethod =
-                implementationClass.lookupProgramMethod(method.getReference());
-            // Don't include methods which are not implemented by the desugared library.
-            if (supported.test(method) && implementationMethod != null) {
-              supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
-            } else {
-              allMethodsAddad = false;
-            }
-          }
-          if (allMethodsAddad) {
-            classesWithAllMethodsSupported.add(clazz);
+          ProgramMethod implementationMethod =
+              implementationClass.lookupProgramMethod(method.getReference());
+          // Don't include methods which are not implemented by the desugared library.
+          if (supported.test(method) && implementationMethod != null) {
+            supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
+          } else {
+            allMethodsAdded = false;
           }
         }
-      }
-
-      // All retargeted methods are supported.
-      for (DexEncodedMethod method : clazz.methods()) {
-        if (desugaredLibrarySpecification
-            .getRetargetCoreLibMember()
-            .keySet()
-            .contains(method.getReference().name)) {
-          if (desugaredLibrarySpecification
-              .getRetargetCoreLibMember()
-              .get(method.getReference().name)
-              .containsKey(clazz.type)) {
-            if (supported.test(method)) {
-              supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
-            }
-          }
+        if (allMethodsAdded) {
+          classesWithAllMethodsSupported.add(clazz);
         }
       }
 
       // All emulated interfaces static and default methods are supported.
-      if (desugaredLibrarySpecification.getEmulateLibraryInterface().containsKey(clazz.type)) {
+      if (desugaredLibrarySpecification.getEmulatedInterfaces().containsKey(clazz.type)) {
         assert clazz.isInterface();
         for (DexEncodedMethod method : clazz.methods()) {
           if (!method.isDefaultMethod() && !method.isStatic()) {
@@ -282,6 +271,21 @@
       }
     }
 
+    // All retargeted methods are supported.
+    desugaredLibrarySpecification.forEachRetargetMethod(
+        method -> {
+          DexClass clazz = dexApplication.contextIndependentDefinitionFor(method.getHolderType());
+          assert clazz != null;
+          DexEncodedMethod encodedMethod = clazz.lookupMethod(method);
+          if (encodedMethod == null) {
+            // Some methods are registered but present higher in the hierarchy, ignore them.
+            return;
+          }
+          if (supported.test(encodedMethod)) {
+            supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(encodedMethod);
+          }
+        });
+
     return new SupportedMethods(classesWithAllMethodsSupported, supportedMethods);
   }
 
@@ -400,6 +404,7 @@
   }
 
   private static class StringBuilderWithIndent {
+
     String NL = System.lineSeparator();
     StringBuilder builder = new StringBuilder();
     String indent = "";
@@ -607,6 +612,7 @@
   }
 
   private static class HTMLBuilder extends StringBuilderWithIndent {
+
     private String indent = "";
 
     private void increaseIndent() {
@@ -648,6 +654,7 @@
   }
 
   public static class HTMLSourceBuilder extends SourceBuilder<HTMLSourceBuilder> {
+
     private final Set<DexMethod> parallelMethods;
 
     public HTMLSourceBuilder(DexClass clazz, boolean newClass, Set<DexMethod> parallelMethods) {
diff --git a/src/main/java/com/android/tools/r8/JarSizeCompare.java b/src/main/java/com/android/tools/r8/JarSizeCompare.java
index e520d1b..60a8e08 100644
--- a/src/main/java/com/android/tools/r8/JarSizeCompare.java
+++ b/src/main/java/com/android/tools/r8/JarSizeCompare.java
@@ -26,6 +26,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -131,7 +132,7 @@
   }
 
   private Map<String, DexProgramClass> translateClassNames(
-      DexApplication input, List<DexProgramClass> classes) {
+      DexApplication input, Collection<DexProgramClass> classes) {
     Map<String, DexProgramClass> result = new HashMap<>();
     ClassNameMapper classNameMapper = input.getProguardMap();
     for (DexProgramClass programClass : classes) {
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 7c0a805..0c8f9ea 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.LazyLoadedDexApplication;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.ir.desugar.TypeRewriter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
 import com.android.tools.r8.jar.CfApplicationWriter;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.PrefixRewritingNamingLens;
@@ -134,6 +135,7 @@
       options.enableLoadStoreOptimization = false;
 
       AppView<AppInfo> appView = readApp(inputApp, options, executor, timing);
+      DesugaredLibraryAmender.run(appView);
 
       if (!options.disableL8AnnotationRemoval) {
         AnnotationRemover.clearAnnotations(appView);
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 6da92e2..8678a9d 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.inspector.Inspector;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
@@ -42,7 +42,7 @@
 
   private final D8Command d8Command;
   private final R8Command r8Command;
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
+  private final DesugaredLibrarySpecification desugaredLibrarySpecification;
   private final DexItemFactory factory;
 
   boolean isShrinking() {
@@ -95,7 +95,7 @@
       Reporter diagnosticsHandler,
       boolean encodeChecksum,
       BiPredicate<String, Long> dexClassChecksumFilter,
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification,
+      DesugaredLibrarySpecification desugaredLibrarySpecification,
       List<AssertionsConfiguration> assertionsConfiguration,
       List<Consumer<Inspector>> outputInspections,
       int threadCount,
@@ -335,7 +335,7 @@
       }
 
       DexItemFactory factory = new DexItemFactory();
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
+      DesugaredLibrarySpecification desugaredLibrarySpecification =
           getDesugaredLibraryConfiguration(factory, true);
 
       R8Command r8Command = null;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8b93d23..ac01496 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -49,6 +49,7 @@
 import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
 import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
 import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
 import com.android.tools.r8.ir.desugar.records.RecordFieldValuesRewriter;
@@ -310,6 +311,7 @@
       if (!options.mainDexKeepRules.isEmpty()) {
         MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
       }
+      DesugaredLibraryAmender.run(appView);
       InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options);
       BackportedMethodRewriter.registerAssumedLibraryTypes(options);
       if (options.enableEnumUnboxing) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 5bc9ddd..4e72575 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -15,7 +15,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.inspector.Inspector;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.naming.SourceFileRewriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
@@ -475,7 +475,7 @@
       List<ProguardConfigurationRule> mainDexKeepRules =
           ProguardConfigurationParser.parse(mainDexRules, factory, reporter);
 
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
+      DesugaredLibrarySpecification desugaredLibrarySpecification =
           getDesugaredLibraryConfiguration(factory, false);
 
       ProguardConfigurationParser parser =
@@ -666,7 +666,7 @@
   private final GraphConsumer mainDexKeptGraphConsumer;
   private final Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer;
   private final StringConsumer desugaredLibraryKeepRuleConsumer;
-  private final LegacyDesugaredLibrarySpecification desugaredLibrarySpecification;
+  private final DesugaredLibrarySpecification desugaredLibrarySpecification;
   private final FeatureSplitConfiguration featureSplitConfiguration;
   private final String synthesizedClassPrefix;
   private final boolean skipDump;
@@ -744,7 +744,7 @@
       boolean encodeChecksum,
       BiPredicate<String, Long> dexClassChecksumFilter,
       StringConsumer desugaredLibraryKeepRuleConsumer,
-      LegacyDesugaredLibrarySpecification desugaredLibrarySpecification,
+      DesugaredLibrarySpecification desugaredLibrarySpecification,
       FeatureSplitConfiguration featureSplitConfiguration,
       List<AssertionsConfiguration> assertionsConfiguration,
       List<Consumer<Inspector>> outputInspections,
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 5d26e0a..1eb78d1 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -383,7 +383,7 @@
       return OriginalSourceFiles.unreachable();
     }
     // Clear all source files so as not to collect the original files.
-    List<DexProgramClass> classes = appView.appInfo().classes();
+    Collection<DexProgramClass> classes = appView.appInfo().classes();
     Map<DexType, DexString> originalSourceFiles = new HashMap<>(classes.size());
     for (DexProgramClass clazz : classes) {
       DexString originalSourceFile = clazz.getSourceFile();
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index cdf0ad1..1e6fb22 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -82,6 +82,18 @@
     return originalFlags;
   }
 
+  public ClassAccessFlags asClassAccessFlags() {
+    return null;
+  }
+
+  public MethodAccessFlags asMethodAccessFlags() {
+    return null;
+  }
+
+  public FieldAccessFlags asFieldAccessFlags() {
+    return null;
+  }
+
   @Override
   public boolean equals(Object object) {
     if (object instanceof AccessFlags) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index ef10e8d..1fac1e6 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.InternalOptions;
-import java.util.List;
+import java.util.Collection;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.function.Consumer;
@@ -122,12 +122,12 @@
     return syntheticItems;
   }
 
-  public List<DexProgramClass> classes() {
+  public Collection<DexProgramClass> classes() {
     assert checkIfObsolete();
     return app.classes();
   }
 
-  public List<DexProgramClass> classesWithDeterministicOrder() {
+  public Collection<DexProgramClass> classesWithDeterministicOrder() {
     assert checkIfObsolete();
     return app.classesWithDeterministicOrder();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index fd28cde..1d05d43 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -166,6 +166,11 @@
   }
 
   @Override
+  public DexMethod getNextMethodSignature(DexMethod method) {
+    return originalMethodSignatures.inverse().getOrDefault(method, method);
+  }
+
+  @Override
   public boolean isContextFreeForMethods() {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 19f0fdf..45d4be7 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -107,6 +107,11 @@
     return flags;
   }
 
+  @Override
+  public ClassAccessFlags asClassAccessFlags() {
+    return this;
+  }
+
   /**
    * Checks whether the constraints from
    * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1 are met.
diff --git a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
index 4e34aa3..4eca4e1 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
@@ -43,6 +43,12 @@
       return this;
     }
 
+    public Builder add(ProgramOrClasspathClass programOrClasspathClass) {
+      assert this.programOrClasspathClass == null;
+      this.programOrClasspathClass = programOrClasspathClass;
+      return this;
+    }
+
     public ClassResolutionResult build() {
       if (programOrClasspathClass == null && libraryClass == null) {
         return noResult();
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 7a06b3d..a260816 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -33,6 +33,10 @@
         + getClass().getCanonicalName());
   }
 
+  public GraphLens getCodeLens(AppView<?> appView) {
+    return appView.codeLens();
+  }
+
   public BytecodeMetadata<? extends CfOrDexInstruction> getMetadata() {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index e08f2ed..feedd6c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -94,9 +94,9 @@
   // may process classes concurrently and fail-fast on the first error.
   private static class ReorderBox<T> {
 
-    private List<T> classes;
+    private Collection<T> classes;
 
-    ReorderBox(List<T> classes) {
+    ReorderBox(Collection<T> classes) {
       this.classes = classes;
     }
 
@@ -109,20 +109,20 @@
       return true;
     }
 
-    List<T> getClasses() {
+    Collection<T> getClasses() {
       return classes;
     }
   }
 
-  abstract List<DexProgramClass> programClasses();
+  abstract Collection<DexProgramClass> programClasses();
 
-  public List<DexProgramClass> classes() {
+  public Collection<DexProgramClass> classes() {
     ReorderBox<DexProgramClass> box = new ReorderBox<>(programClasses());
     assert box.reorderClasses();
     return box.getClasses();
   }
 
-  public List<DexProgramClass> classesWithDeterministicOrder() {
+  public Collection<DexProgramClass> classesWithDeterministicOrder() {
     return classesWithDeterministicOrder(new ArrayList<>(programClasses()));
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index c326dc0..c33d18d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -44,6 +44,7 @@
 import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind;
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumericType;
 import com.android.tools.r8.ir.code.ValueType;
@@ -85,7 +86,6 @@
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
-import java.util.function.IntPredicate;
 import org.objectweb.asm.Opcodes;
 
 public class DexEncodedMethod extends DexEncodedMember<DexEncodedMethod, DexMethod>
@@ -1470,21 +1470,31 @@
       return this;
     }
 
-    public Builder removeParameterAnnotations(IntPredicate predicate) {
+    public Builder rewriteParameterAnnotations(
+        DexEncodedMethod method, ArgumentInfoCollection argumentInfoCollection) {
       if (parameterAnnotations.isEmpty()) {
         // Nothing to do.
         return this;
       }
+      if (!argumentInfoCollection.hasArgumentPermutation()
+          && !argumentInfoCollection.hasRemovedArguments()) {
+        // Nothing to do.
+        return this;
+      }
 
-      List<DexAnnotationSet> newParameterAnnotations = new ArrayList<>();
+      List<DexAnnotationSet> newParameterAnnotations =
+          new ArrayList<>(parameterAnnotations.countNonMissing());
       int newNumberOfMissingParameterAnnotations = 0;
 
-      for (int oldIndex = 0; oldIndex < parameterAnnotations.size(); oldIndex++) {
-        if (!predicate.test(oldIndex)) {
-          if (parameterAnnotations.isMissing(oldIndex)) {
+      for (int parameterIndex = 0;
+          parameterIndex < method.getParameters().size();
+          parameterIndex++) {
+        int argumentIndex = parameterIndex + method.getFirstNonReceiverArgumentIndex();
+        if (!argumentInfoCollection.isArgumentRemoved(argumentIndex)) {
+          if (parameterAnnotations.isMissing(parameterIndex)) {
             newNumberOfMissingParameterAnnotations++;
           } else {
-            newParameterAnnotations.add(parameterAnnotations.get(oldIndex));
+            newParameterAnnotations.add(parameterAnnotations.get(parameterIndex));
           }
         }
       }
@@ -1493,6 +1503,23 @@
         return setParameterAnnotations(ParameterAnnotationsList.empty());
       }
 
+      if (argumentInfoCollection.hasArgumentPermutation()) {
+        List<DexAnnotationSet> newPermutedParameterAnnotations =
+            Arrays.asList(new DexAnnotationSet[method.getParameters().size()]);
+        for (int parameterIndex = newNumberOfMissingParameterAnnotations;
+            parameterIndex < method.getParameters().size();
+            parameterIndex++) {
+          int argumentIndex = parameterIndex + method.getFirstNonReceiverArgumentIndex();
+          int newArgumentIndex = argumentInfoCollection.getNewArgumentIndex(argumentIndex, 0);
+          int newParameterIndex = newArgumentIndex - method.getFirstNonReceiverArgumentIndex();
+          newPermutedParameterAnnotations.set(
+              newParameterIndex,
+              newParameterAnnotations.get(parameterIndex - newNumberOfMissingParameterAnnotations));
+        }
+        newParameterAnnotations = newPermutedParameterAnnotations;
+        newNumberOfMissingParameterAnnotations = 0;
+      }
+
       return setParameterAnnotations(
           ParameterAnnotationsList.create(
               newParameterAnnotations.toArray(DexAnnotationSet.EMPTY_ARRAY),
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 8e96fba..dd95a2d 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -10,17 +10,19 @@
 
 import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.graph.LazyLoadedDexApplication.AllClasses;
-import com.android.tools.r8.graph.classmerging.MergedClasses;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 public class DirectMappedDexApplication extends DexApplication {
 
@@ -29,63 +31,86 @@
   private final Map<Code, DexEncodedMethod> codeOwners = new IdentityHashMap<>();
 
   // Unmodifiable mapping of all types to their definitions.
-  private final Map<DexType, DexClass> allClasses;
-  // Collections of the three different types for iteration.
-  private final ImmutableList<DexProgramClass> programClasses;
-  private final ImmutableList<DexClasspathClass> classpathClasses;
-  private final ImmutableList<DexLibraryClass> libraryClasses;
+  private final ImmutableMap<DexType, ProgramOrClasspathClass> programOrClasspathClasses;
+  private final ImmutableMap<DexType, DexLibraryClass> libraryClasses;
+
+  // Collections of different types for iteration.
+  private final ImmutableCollection<DexProgramClass> programClasses;
+  private final ImmutableCollection<DexClasspathClass> classpathClasses;
 
   private DirectMappedDexApplication(
       ClassNameMapper proguardMap,
       DexApplicationReadFlags flags,
-      Map<DexType, DexClass> allClasses,
-      ImmutableList<DexProgramClass> programClasses,
-      ImmutableList<DexClasspathClass> classpathClasses,
-      ImmutableList<DexLibraryClass> libraryClasses,
+      ImmutableMap<DexType, ProgramOrClasspathClass> programOrClasspathClasses,
+      ImmutableMap<DexType, DexLibraryClass> libraryClasses,
+      ImmutableCollection<DexProgramClass> programClasses,
+      ImmutableCollection<DexClasspathClass> classpathClasses,
       ImmutableList<DataResourceProvider> dataResourceProviders,
       InternalOptions options,
       DexString highestSortingString,
       Timing timing) {
     super(proguardMap, flags, dataResourceProviders, options, highestSortingString, timing);
-    this.allClasses = Collections.unmodifiableMap(allClasses);
+    this.programOrClasspathClasses = programOrClasspathClasses;
+    this.libraryClasses = libraryClasses;
     this.programClasses = programClasses;
     this.classpathClasses = classpathClasses;
-    this.libraryClasses = libraryClasses;
   }
 
-  public Collection<DexClass> allClasses() {
-    return allClasses.values();
-  }
-
-  public List<DexClasspathClass> classpathClasses() {
+  public Collection<DexClasspathClass> classpathClasses() {
     return classpathClasses;
   }
 
   @Override
-  List<DexProgramClass> programClasses() {
+  Collection<DexProgramClass> programClasses() {
     return programClasses;
   }
 
-  public List<DexLibraryClass> libraryClasses() {
-    return libraryClasses;
+  public Collection<DexLibraryClass> libraryClasses() {
+    return libraryClasses.values();
   }
 
   @Override
   public ClassResolutionResult contextIndependentDefinitionForWithResolutionResult(DexType type) {
-    DexClass foundClass = definitionFor(type);
-    return foundClass == null ? noResult() : foundClass;
+    assert type.isClassType() : "Cannot lookup definition for type: " + type;
+    DexLibraryClass libraryClass = libraryClasses.get(type);
+    ProgramOrClasspathClass programOrClasspathClass = programOrClasspathClasses.get(type);
+    if (libraryClass == null && programOrClasspathClass == null) {
+      return noResult();
+    } else if (libraryClass != null && programOrClasspathClass == null) {
+      return libraryClass;
+    } else if (libraryClass == null) {
+      return programOrClasspathClass.asDexClass();
+    } else {
+      return ClassResolutionResult.builder().add(libraryClass).add(programOrClasspathClass).build();
+    }
   }
 
   @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
-    return allClasses.get(type);
+    if (options.lookupLibraryBeforeProgram) {
+      DexLibraryClass libraryClass = libraryClasses.get(type);
+      if (libraryClass != null) {
+        return libraryClass;
+      }
+      ProgramOrClasspathClass programOrClasspathClass = programOrClasspathClasses.get(type);
+      return programOrClasspathClass != null ? programOrClasspathClass.asDexClass() : null;
+    } else {
+      ProgramOrClasspathClass programOrClasspathClass = programOrClasspathClasses.get(type);
+      if (programOrClasspathClass != null && programOrClasspathClass.isProgramClass()) {
+        return programOrClasspathClass.asDexClass();
+      }
+      DexLibraryClass libraryClass = libraryClasses.get(type);
+      return libraryClass != null
+          ? libraryClass
+          : (programOrClasspathClass == null ? null : programOrClasspathClass.asDexClass());
+    }
   }
 
   @Override
   public DexProgramClass programDefinitionFor(DexType type) {
-    // The direct mapped application has no duplicates so this coincides with definitionFor.
-    return DexProgramClass.asProgramClassOrNull(definitionFor(type));
+    ProgramOrClasspathClass programOrClasspathClass = programOrClasspathClasses.get(type);
+    return programOrClasspathClass == null ? null : programOrClasspathClass.asProgramClass();
   }
 
   @Override
@@ -119,21 +144,8 @@
     return true;
   }
 
-  public boolean verifyNothingToRewrite(AppView<?> appView, GraphLens lens) {
-    assert allClasses.keySet().stream()
-        .allMatch(
-            type ->
-                lens.lookupType(type) == type
-                    || MergedClasses.hasBeenMergedIntoDifferentType(
-                        appView.verticallyMergedClasses(), type)
-                    || MergedClasses.hasBeenMergedIntoDifferentType(
-                        appView.horizontallyMergedClasses(), type));
-    assert verifyCodeObjectsOwners();
-    return true;
-  }
-
   private boolean mappingIsValid(
-      List<DexProgramClass> classesBeforeLensApplication, GraphLens lens) {
+      Collection<DexProgramClass> classesBeforeLensApplication, GraphLens lens) {
     // The lens might either map to a different type that is already present in the application
     // (e.g. relinking a type) or it might encode a type that was renamed, in which case the
     // original type will point to a definition that was renamed.
@@ -188,19 +200,19 @@
 
   public static class Builder extends DexApplication.Builder<Builder> {
 
-    private ImmutableList<DexClasspathClass> classpathClasses;
-    private ImmutableList<DexLibraryClass> libraryClasses;
+    private ImmutableCollection<DexClasspathClass> classpathClasses;
+    private Map<DexType, DexLibraryClass> libraryClasses;
 
     private final List<DexClasspathClass> pendingClasspathClasses = new ArrayList<>();
+    private final Set<DexType> pendingClasspathRemovalIfPresent = Sets.newIdentityHashSet();
 
     Builder(LazyLoadedDexApplication application) {
       super(application);
       // As a side-effect, this will force-load all classes.
       AllClasses allClasses = application.loadAllClasses();
-      classpathClasses = allClasses.getClasspathClasses();
+      classpathClasses = allClasses.getClasspathClasses().values();
       libraryClasses = allClasses.getLibraryClasses();
-      replaceProgramClasses(allClasses.getProgramClasses());
-      replaceClasspathClasses(allClasses.getClasspathClasses());
+      replaceProgramClasses(allClasses.getProgramClasses().values());
     }
 
     private Builder(DirectMappedDexApplication application) {
@@ -222,32 +234,17 @@
     @Override
     public void addProgramClassPotentiallyOverridingNonProgramClass(DexProgramClass clazz) {
       addProgramClass(clazz);
-      if (containsType(clazz.type, libraryClasses)) {
-        replaceLibraryClasses(withoutType(clazz.type, libraryClasses));
-        return;
-      }
-      if (containsType(clazz.type, classpathClasses)) {
-        replaceClasspathClasses(withoutType(clazz.type, classpathClasses));
+      pendingClasspathRemovalIfPresent.add(clazz.type);
+      if (libraryClasses.containsKey(clazz.type)) {
+        ensureMutableLibraryClassesMap();
+        libraryClasses.remove(clazz.type);
       }
     }
 
-    private boolean containsType(DexType type, List<? extends DexClass> classes) {
-      for (DexClass clazz : classes) {
-        if (clazz.type == type) {
-          return true;
-        }
+    private void ensureMutableLibraryClassesMap() {
+      if (!(libraryClasses instanceof IdentityHashMap)) {
+        libraryClasses = new IdentityHashMap<>(libraryClasses);
       }
-      return false;
-    }
-
-    private <T extends DexClass> ImmutableList<T> withoutType(DexType type, List<T> classes) {
-      ImmutableList.Builder<T> builder = ImmutableList.builder();
-      for (T clazz : classes) {
-        if (clazz.type != type) {
-          builder.add(clazz);
-        }
-      }
-      return builder.build();
     }
 
     @Override
@@ -260,11 +257,6 @@
       return self();
     }
 
-    public Builder addClasspathClasses(Collection<DexClasspathClass> classes) {
-      pendingClasspathClasses.addAll(classes);
-      return self();
-    }
-
     private void commitPendingClasspathClasses() {
       if (!pendingClasspathClasses.isEmpty()) {
         classpathClasses =
@@ -276,11 +268,6 @@
       }
     }
 
-    public List<DexClasspathClass> getClasspathClasses() {
-      commitPendingClasspathClasses();
-      return classpathClasses;
-    }
-
     public Builder replaceClasspathClasses(Collection<DexClasspathClass> newClasspathClasses) {
       assert newClasspathClasses != null;
       classpathClasses = ImmutableList.copyOf(newClasspathClasses);
@@ -289,13 +276,9 @@
     }
 
     public Builder replaceLibraryClasses(Collection<DexLibraryClass> libraryClasses) {
-      this.libraryClasses = ImmutableList.copyOf(libraryClasses);
-      return self();
-    }
-
-    public Builder addLibraryClasses(Collection<DexLibraryClass> classes) {
-      libraryClasses =
-          ImmutableList.<DexLibraryClass>builder().addAll(libraryClasses).addAll(classes).build();
+      ImmutableMap.Builder<DexType, DexLibraryClass> builder = ImmutableMap.builder();
+      libraryClasses.forEach(clazz -> builder.put(clazz.type, clazz));
+      this.libraryClasses = builder.build();
       return self();
     }
 
@@ -303,33 +286,56 @@
     public DirectMappedDexApplication build() {
       // Rebuild the map. This will fail if keys are not unique.
       // TODO(zerny): Consider not rebuilding the map if no program classes are added.
-      Map<DexType, DexClass> allClasses =
-          new IdentityHashMap<>(
-              getProgramClasses().size() + getClasspathClasses().size() + libraryClasses.size());
+      commitPendingClasspathClasses();
+      Map<DexType, ProgramOrClasspathClass> programAndClasspathClasses =
+          new IdentityHashMap<>(getProgramClasses().size() + classpathClasses.size());
       // Note: writing classes in reverse priority order, so a duplicate will be correctly ordered.
-      // There should never be duplicates and that is asserted in the addAll subroutine.
-      addAll(allClasses, libraryClasses);
-      addAll(allClasses, getClasspathClasses());
-      addAll(allClasses, getProgramClasses());
+      // There should not be duplicates between program and classpath and that is asserted in the
+      // addAll subroutine.
+      ImmutableCollection<DexClasspathClass> newClasspathClasses = classpathClasses;
+      if (addAll(programAndClasspathClasses, classpathClasses)) {
+        ImmutableList.Builder<DexClasspathClass> builder = ImmutableList.builder();
+        for (DexClasspathClass classpathClass : classpathClasses) {
+          if (!pendingClasspathRemovalIfPresent.contains(classpathClass.getType())) {
+            builder.add(classpathClass);
+          }
+        }
+        newClasspathClasses = builder.build();
+      }
+      addAll(programAndClasspathClasses, getProgramClasses());
       return new DirectMappedDexApplication(
           proguardMap,
           flags,
-          allClasses,
+          ImmutableMap.copyOf(programAndClasspathClasses),
+          getLibraryClassesAsImmutableMap(),
           ImmutableList.copyOf(getProgramClasses()),
-          ImmutableList.copyOf(getClasspathClasses()),
-          libraryClasses,
+          newClasspathClasses,
           ImmutableList.copyOf(dataResourceProviders),
           options,
           highestSortingString,
           timing);
     }
-  }
 
-  private static <T extends DexClass> void addAll(
-      Map<DexType, DexClass> allClasses, Iterable<T> toAdd) {
-    for (DexClass clazz : toAdd) {
-      DexClass old = allClasses.put(clazz.type, clazz);
-      assert old == null : "Class " + old.type.toString() + " was already present.";
+    private <T extends ProgramOrClasspathClass> boolean addAll(
+        Map<DexType, ProgramOrClasspathClass> allClasses, Iterable<T> toAdd) {
+      boolean seenRemoved = false;
+      for (T clazz : toAdd) {
+        if (clazz.isProgramClass() || !pendingClasspathRemovalIfPresent.contains(clazz.getType())) {
+          ProgramOrClasspathClass old = allClasses.put(clazz.getType(), clazz);
+          assert old == null : "Class " + old.getType().toString() + " was already present.";
+        } else {
+          seenRemoved = true;
+        }
+      }
+      return seenRemoved;
+    }
+
+    private ImmutableMap<DexType, DexLibraryClass> getLibraryClassesAsImmutableMap() {
+      if (libraryClasses instanceof ImmutableMap) {
+        return (ImmutableMap<DexType, DexLibraryClass>) libraryClasses;
+      } else {
+        return ImmutableMap.copyOf(libraryClasses);
+      }
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
index 95b2a6f..cc2e10c 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
@@ -99,6 +99,11 @@
   }
 
   @Override
+  public FieldAccessFlags asFieldAccessFlags() {
+    return this;
+  }
+
+  @Override
   public int getAsCfAccessFlags() {
     return materialize();
   }
@@ -138,6 +143,11 @@
       super(FieldAccessFlags.fromSharedAccessFlags(0));
     }
 
+    public Builder set(int flag) {
+      flags.set(flag);
+      return this;
+    }
+
     @Override
     public Builder self() {
       return this;
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
index 8fa722d..950e824 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.shaking.KeepMethodInfo;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -106,7 +107,7 @@
     return new GenericSignatureCorrectnessHelper(appView, contextBuilder, Mode.VERIFY);
   }
 
-  public SignatureEvaluationResult run(List<DexProgramClass> programClasses) {
+  public SignatureEvaluationResult run(Collection<DexProgramClass> programClasses) {
     if (appView.options().disableGenericSignatureValidation
         || !appView.options().parseSignatureAttribute()) {
       return VALID;
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
index ccbb86f..2599a50 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
+import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.google.common.collect.Sets;
@@ -42,7 +43,10 @@
 
   @Override
   public void processNewlyLiveMethod(
-      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
+      ProgramMethod method,
+      ProgramDefinition context,
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist) {
     processSignature(method, context);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 3041ccf..952c31d 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -543,6 +543,10 @@
     return false;
   }
 
+  public boolean isHorizontalClassMergerGraphLens() {
+    return false;
+  }
+
   public abstract boolean isIdentityLens();
 
   public boolean isMemberRebindingLens() {
@@ -911,6 +915,8 @@
 
     public abstract DexMethod getPreviousMethodSignature(DexMethod method);
 
+    public abstract DexMethod getNextMethodSignature(DexMethod method);
+
     @Override
     public final boolean isIdentityLens() {
       return false;
@@ -1122,6 +1128,11 @@
     }
 
     @Override
+    public DexMethod getNextMethodSignature(DexMethod method) {
+      return method;
+    }
+
+    @Override
     public boolean isContextFreeForMethods() {
       return getIdentityLens().isContextFreeForMethods();
     }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
index 772227c..b781f0b 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyLoadedDexApplication.java
@@ -15,13 +15,13 @@
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 public class LazyLoadedDexApplication extends DexApplication {
@@ -119,92 +119,102 @@
   static class AllClasses {
 
     // Mapping of all types to their definitions.
-    private final Map<DexType, DexClass> allClasses;
     // Collections of the three different types for iteration.
-    private final ImmutableList<DexProgramClass> programClasses;
-    private final ImmutableList<DexClasspathClass> classpathClasses;
-    private final ImmutableList<DexLibraryClass> libraryClasses;
+    private final ImmutableMap<DexType, DexProgramClass> programClasses;
+    private final ImmutableMap<DexType, DexClasspathClass> classpathClasses;
+    private final ImmutableMap<DexType, DexLibraryClass> libraryClasses;
 
     AllClasses(
         LibraryClassCollection libraryClassesLoader,
         ClasspathClassCollection classpathClassesLoader,
         ProgramClassCollection programClassesLoader,
         InternalOptions options) {
-      int expectedMaxSize = 0;
 
       // Force-load library classes.
-      Map<DexType, DexLibraryClass> allLibraryClasses = null;
+      ImmutableMap<DexType, DexLibraryClass> allLibraryClasses;
       if (libraryClassesLoader != null) {
         libraryClassesLoader.forceLoad(type -> true);
         allLibraryClasses = libraryClassesLoader.getAllClassesInMap();
-        expectedMaxSize += allLibraryClasses.size();
+      } else {
+        allLibraryClasses = ImmutableMap.of();
       }
 
       // Program classes should be fully loaded.
       assert programClassesLoader != null;
       assert programClassesLoader.isFullyLoaded();
       programClassesLoader.forceLoad(type -> true);
-      Map<DexType, DexProgramClass> allProgramClasses = programClassesLoader.getAllClassesInMap();
-      expectedMaxSize += allProgramClasses.size();
+      ImmutableMap<DexType, DexProgramClass> allProgramClasses =
+          programClassesLoader.getAllClassesInMap();
 
       // Force-load classpath classes.
-      Map<DexType, DexClasspathClass> allClasspathClasses = null;
+      ImmutableMap<DexType, DexClasspathClass> allClasspathClasses;
       if (classpathClassesLoader != null) {
         classpathClassesLoader.forceLoad(type -> true);
         allClasspathClasses = classpathClassesLoader.getAllClassesInMap();
-        expectedMaxSize += allClasspathClasses.size();
+      } else {
+        allClasspathClasses = ImmutableMap.of();
       }
 
       // Collect loaded classes in the precedence order library classes, program classes and
       // class path classes or program classes, classpath classes and library classes depending
       // on the configured lookup order.
-      Map<DexType, DexClass> prioritizedClasses = new IdentityHashMap<>(expectedMaxSize);
-      if (options.lookupLibraryBeforeProgram) {
-        libraryClasses = fillPrioritizedClasses(allLibraryClasses, prioritizedClasses, options);
-        programClasses = fillPrioritizedClasses(allProgramClasses, prioritizedClasses, options);
-        classpathClasses = fillPrioritizedClasses(allClasspathClasses, prioritizedClasses, options);
+      if (options.loadAllClassDefinitions) {
+        libraryClasses = allLibraryClasses;
+        programClasses = allProgramClasses;
+        classpathClasses = allClasspathClasses;
       } else {
-        programClasses = fillPrioritizedClasses(allProgramClasses, prioritizedClasses, options);
-        classpathClasses = fillPrioritizedClasses(allClasspathClasses, prioritizedClasses, options);
-        libraryClasses = fillPrioritizedClasses(allLibraryClasses, prioritizedClasses, options);
+        if (options.lookupLibraryBeforeProgram) {
+          libraryClasses = fillPrioritizedClasses(allLibraryClasses, type -> null, options);
+          programClasses = fillPrioritizedClasses(allProgramClasses, libraryClasses::get, options);
+          classpathClasses =
+              fillPrioritizedClasses(
+                  allClasspathClasses,
+                  type -> {
+                    DexLibraryClass clazz = libraryClasses.get(type);
+                    return clazz != null ? clazz : programClasses.get(type);
+                  },
+                  options);
+        } else {
+          programClasses = fillPrioritizedClasses(allProgramClasses, type -> null, options);
+          classpathClasses =
+              fillPrioritizedClasses(allClasspathClasses, programClasses::get, options);
+          libraryClasses =
+              fillPrioritizedClasses(
+                  allLibraryClasses,
+                  type -> {
+                    DexProgramClass clazz = programClasses.get(type);
+                    return clazz != null ? clazz : classpathClasses.get(type);
+                  },
+                  options);
+        }
       }
-
-      allClasses = Collections.unmodifiableMap(prioritizedClasses);
-
-      assert prioritizedClasses.size()
-          == libraryClasses.size() + classpathClasses.size() + programClasses.size();
     }
 
-    public Map<DexType, DexClass> getAllClasses() {
-      return allClasses;
-    }
-
-    public ImmutableList<DexProgramClass> getProgramClasses() {
+    public ImmutableMap<DexType, DexProgramClass> getProgramClasses() {
       return programClasses;
     }
 
-    public ImmutableList<DexClasspathClass> getClasspathClasses() {
+    public ImmutableMap<DexType, DexClasspathClass> getClasspathClasses() {
       return classpathClasses;
     }
 
-    public ImmutableList<DexLibraryClass> getLibraryClasses() {
+    public ImmutableMap<DexType, DexLibraryClass> getLibraryClasses() {
       return libraryClasses;
     }
   }
 
-  private static <T extends DexClass> ImmutableList<T> fillPrioritizedClasses(
+  private static <T extends DexClass> ImmutableMap<DexType, T> fillPrioritizedClasses(
       Map<DexType, T> classCollection,
-      Map<DexType, DexClass> prioritizedClasses,
+      Function<DexType, DexClass> getExisting,
       InternalOptions options) {
     if (classCollection != null) {
       Set<DexType> javaLibraryOverride = Sets.newIdentityHashSet();
-      ImmutableList.Builder<T> builder = ImmutableList.builder();
+      ImmutableMap.Builder<DexType, T> builder = ImmutableMap.builder();
       classCollection.forEach(
           (type, clazz) -> {
-            DexClass other = prioritizedClasses.get(type);
+            DexClass other = getExisting.apply(type);
             if (other == null) {
-              prioritizedClasses.put(type, clazz);
-              builder.add(clazz);
+              builder.put(type, clazz);
             } else if (type.getPackageName().startsWith("java.")
                 && (clazz.isLibraryClass() || other.isLibraryClass())) {
               javaLibraryOverride.add(type);
@@ -215,12 +225,15 @@
       }
       return builder.build();
     } else {
-      return ImmutableList.of();
+      return ImmutableMap.of();
     }
   }
 
   private static void warnJavaLibraryOverride(
       InternalOptions options, Set<DexType> javaLibraryOverride) {
+    if (options.ignoreJavaLibraryOverride) {
+      return;
+    }
     String joined =
         javaLibraryOverride.stream()
             .sorted()
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index cd5e8de..a20ab1d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -119,6 +119,11 @@
     return copy.materialize();
   }
 
+  @Override
+  public MethodAccessFlags asMethodAccessFlags() {
+    return this;
+  }
+
   public boolean isSynchronized() {
     return isSet(Constants.ACC_SYNCHRONIZED);
   }
@@ -249,6 +254,11 @@
       super(MethodAccessFlags.fromSharedAccessFlags(0, false));
     }
 
+    public Builder set(int flag) {
+      flags.set(flag);
+      return this;
+    }
+
     public Builder setBridge() {
       flags.setBridge();
       return this;
diff --git a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
index 87e55e9..fe15a02 100644
--- a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
@@ -145,7 +145,7 @@
       return originalMethod;
     }
     DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
-    return internalGetNextMethodSignature(renamedMethod);
+    return getNextMethodSignature(renamedMethod);
   }
 
   @Override
@@ -257,7 +257,8 @@
     return newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
   }
 
-  protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+  @Override
+  public DexMethod getNextMethodSignature(DexMethod method) {
     return newMethodSignatures.getRepresentativeValueOrDefault(method, method);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index 2382abc..b61f288 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -9,7 +9,6 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.ArrayDeque;
-import java.util.Collection;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -49,11 +48,15 @@
   }
 
   public static SubtypingInfo create(AppInfoWithClassHierarchy appInfo) {
-    return create(appInfo.app().asDirect().allClasses(), appInfo);
+    DirectMappedDexApplication directApp = appInfo.app().asDirect();
+    return create(
+        Iterables.concat(
+            directApp.programClasses(), directApp.classpathClasses(), directApp.libraryClasses()),
+        appInfo);
   }
 
   public static SubtypingInfo create(
-      Collection<? extends DexClass> classes, DexDefinitionSupplier definitions) {
+      Iterable<? extends DexClass> classes, DexDefinitionSupplier definitions) {
     Map<DexType, TypeInfo> typeInfo = new ConcurrentHashMap<>();
     Map<DexType, Set<DexType>> subtypeMap = new IdentityHashMap<>();
     populateSubtypeMap(classes, subtypeMap, typeInfo, definitions);
@@ -117,7 +120,7 @@
   }
 
   private static void populateSubtypeMap(
-      Collection<? extends DexClass> classes,
+      Iterable<? extends DexClass> classes,
       Map<DexType, Set<DexType>> map,
       Map<DexType, TypeInfo> typeInfo,
       DexDefinitionSupplier definitionSupplier) {
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 09d477d..1ce97e6 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -41,7 +41,8 @@
   }
 
   public GraphLens getCodeLens() {
-    return appView.codeLens();
+    assert context.isMethod();
+    return getMethodContext().getDefinition().getCode().getCodeLens(appView);
   }
 
   public final T getContext() {
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index 6e6088a..1321169 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
+import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 
 public class ApiModelAnalysis extends EnqueuerAnalysis {
@@ -36,7 +37,10 @@
 
   @Override
   public void processNewlyLiveMethod(
-      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
+      ProgramMethod method,
+      ProgramDefinition context,
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist) {
     computeAndSetApiLevelForDefinition(method);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index d110049..9f9d30a 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -25,6 +25,7 @@
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
 import com.google.common.collect.ImmutableList;
@@ -77,7 +78,10 @@
 
   @Override
   public void processNewlyLiveMethod(
-      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
+      ProgramMethod method,
+      ProgramDefinition context,
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist) {
     DexEncodedMethod definition = method.getDefinition();
     if (!definition.hasCode() || !definition.getCode().isCfCode()) {
       return;
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index eef04d0..8dc26d6 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -30,7 +30,10 @@
 
   /** Called when a method is found to be live. */
   public void processNewlyLiveMethod(
-      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {}
+      ProgramMethod method,
+      ProgramDefinition context,
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist) {}
 
   /** Called when a method's code has been processed by the registry. */
   public void processTracedCode(
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfo.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfo.java
index 26cf24f..d221385 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfo.java
@@ -52,6 +52,10 @@
     return null;
   }
 
+  public boolean isRemovedReceiverInfo() {
+    return false;
+  }
+
   public boolean isRewrittenTypeInfo() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
index 0a9fcbf..cbfa83d 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/ArgumentInfoCollection.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.ConsumerUtils;
 import com.android.tools.r8.utils.IntObjConsumer;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
 import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
@@ -344,13 +343,6 @@
    */
   public Consumer<DexEncodedMethod.Builder> createParameterAnnotationsRemover(
       DexEncodedMethod method) {
-    if (numberOfRemovedArguments() > 0 && !method.parameterAnnotationsList.isEmpty()) {
-      return builder -> {
-        int firstArgumentIndex = method.getFirstNonReceiverArgumentIndex();
-        builder.removeParameterAnnotations(
-            oldIndex -> getArgumentInfo(oldIndex + firstArgumentIndex).isRemovedArgumentInfo());
-      };
-    }
-    return ConsumerUtils.emptyConsumer();
+    return builder -> builder.rewriteParameterAnnotations(method, this);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java b/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java
index 953cedd..79b3b6c 100644
--- a/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/proto/RemovedArgumentInfo.java
@@ -13,39 +13,41 @@
 
 public class RemovedArgumentInfo extends ArgumentInfo {
 
-  public static class Builder {
+  abstract static class BuilderBase<B extends BuilderBase<B>> {
 
-    private boolean checkNullOrZero;
-    private SingleValue singleValue;
-    private DexType type;
+    SingleValue singleValue;
+    DexType type;
 
-    public Builder setCheckNullOrZero(boolean checkNullOrZero) {
-      this.checkNullOrZero = checkNullOrZero;
-      return this;
-    }
-
-    public Builder setSingleValue(SingleValue singleValue) {
+    public B setSingleValue(SingleValue singleValue) {
       this.singleValue = singleValue;
-      return this;
+      return self();
     }
 
-    public Builder setType(DexType type) {
+    public B setType(DexType type) {
       this.type = type;
-      return this;
+      return self();
     }
 
+    abstract B self();
+  }
+
+  public static class Builder extends BuilderBase<Builder> {
+
     public RemovedArgumentInfo build() {
-      assert type != null;
-      return new RemovedArgumentInfo(checkNullOrZero, singleValue, type);
+      return new RemovedArgumentInfo(singleValue, type);
+    }
+
+    @Override
+    Builder self() {
+      return this;
     }
   }
 
-  private final boolean checkNullOrZero;
   private final SingleValue singleValue;
   private final DexType type;
 
-  private RemovedArgumentInfo(boolean checkNullOrZero, SingleValue singleValue, DexType type) {
-    this.checkNullOrZero = checkNullOrZero;
+  RemovedArgumentInfo(SingleValue singleValue, DexType type) {
+    assert type != null;
     this.singleValue = singleValue;
     this.type = type;
   }
@@ -66,10 +68,6 @@
     return type;
   }
 
-  public boolean isCheckNullOrZeroSet() {
-    return checkNullOrZero;
-  }
-
   @Override
   public boolean isRemovedArgumentInfo() {
     return true;
@@ -93,7 +91,7 @@
         hasSingleValue() ? singleValue.rewrittenWithLens(appView, graphLens, codeLens) : null;
     DexType rewrittenType = graphLens.lookupType(type, codeLens);
     if (rewrittenSingleValue != singleValue || rewrittenType != type) {
-      return new RemovedArgumentInfo(checkNullOrZero, rewrittenSingleValue, rewrittenType);
+      return new RemovedArgumentInfo(rewrittenSingleValue, rewrittenType);
     }
     return this;
   }
@@ -104,13 +102,11 @@
       return false;
     }
     RemovedArgumentInfo other = (RemovedArgumentInfo) obj;
-    return checkNullOrZero == other.checkNullOrZero
-        && type == other.type
-        && Objects.equals(singleValue, other.singleValue);
+    return type == other.type && Objects.equals(singleValue, other.singleValue);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(checkNullOrZero, singleValue, type);
+    return Objects.hash(singleValue, type);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/proto/RemovedReceiverInfo.java b/src/main/java/com/android/tools/r8/graph/proto/RemovedReceiverInfo.java
new file mode 100644
index 0000000..7bd6530
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/proto/RemovedReceiverInfo.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph.proto;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Objects;
+
+public class RemovedReceiverInfo extends RemovedArgumentInfo {
+
+  RemovedReceiverInfo(SingleValue singleValue, DexType type) {
+    super(singleValue, type);
+  }
+
+  @Override
+  public boolean isRemovedReceiverInfo() {
+    return true;
+  }
+
+  @Override
+  public RemovedReceiverInfo rewrittenWithLens(
+      AppView<AppInfoWithLiveness> appView, GraphLens graphLens, GraphLens codeLens) {
+    SingleValue rewrittenSingleValue =
+        hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens, codeLens) : null;
+    DexType rewrittenType = graphLens.lookupType(getType(), codeLens);
+    if (rewrittenSingleValue != getSingleValue() || rewrittenType != getType()) {
+      return new RemovedReceiverInfo(rewrittenSingleValue, rewrittenType);
+    }
+    return this;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    RemovedReceiverInfo other = (RemovedReceiverInfo) obj;
+    return getType() == other.getType() && Objects.equals(getSingleValue(), other.getSingleValue());
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(getSingleValue(), getType());
+  }
+
+  public static class Builder extends BuilderBase<Builder> {
+
+    public static Builder create() {
+      return new Builder();
+    }
+
+    public RemovedReceiverInfo build() {
+      return new RemovedReceiverInfo(singleValue, type);
+    }
+
+    @Override
+    Builder self() {
+      return this;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index d7dc429..78a8743 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -41,6 +41,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * The class merger is responsible for moving methods from the sources in {@link ClassMerger#group}
@@ -195,15 +196,22 @@
 
   void mergeMethods(
       SyntheticArgumentClass syntheticArgumentClass,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
-    mergeVirtualMethods();
+      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
+    mergeVirtualMethods(virtuallyMergedMethodsKeepInfoConsumer);
     mergeDirectMethods(syntheticArgumentClass, syntheticInitializerConverterBuilder);
     classMethodsBuilder.setClassMethods(group.getTarget());
   }
 
-  void mergeVirtualMethods() {
+  void mergeVirtualMethods(
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     virtualMethodMergers.forEach(
-        merger -> merger.merge(classMethodsBuilder, lensBuilder, classIdentifiers));
+        merger ->
+            merger.merge(
+                classMethodsBuilder,
+                lensBuilder,
+                classIdentifiers,
+                virtuallyMergedMethodsKeepInfoConsumer));
     group.forEachSource(clazz -> clazz.getMethodCollection().clearVirtualMethods());
   }
 
@@ -318,13 +326,17 @@
   public void mergeGroup(
       PrunedItems.Builder prunedItemsBuilder,
       SyntheticArgumentClass syntheticArgumentClass,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
+      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     fixAccessFlags();
     fixNestMemberAttributes();
     mergeAnnotations();
     mergeInterfaces();
     mergeFields(prunedItemsBuilder);
-    mergeMethods(syntheticArgumentClass, syntheticInitializerConverterBuilder);
+    mergeMethods(
+        syntheticArgumentClass,
+        syntheticInitializerConverterBuilder,
+        virtuallyMergedMethodsKeepInfoConsumer);
     group.getTarget().clearClassSignature();
     group.getTarget().forEachProgramMember(ProgramMember::clearGenericSignature);
     group.forEachSource(clazz -> prunedItemsBuilder.addRemovedClass(clazz.getType()));
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index ad8d9df..dd7de45 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -4,17 +4,24 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
+
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
+import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
 import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.TraversalContinuation;
 import java.util.ArrayList;
@@ -23,6 +30,7 @@
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
 
 public class HorizontalClassMerger {
 
@@ -103,9 +111,13 @@
             : null;
     SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder =
         SyntheticInitializerConverter.builder(appView, codeProvider);
+    List<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfos = new ArrayList<>();
     PrunedItems prunedItems =
         applyClassMergers(
-            classMergers, syntheticArgumentClass, syntheticInitializerConverterBuilder);
+            classMergers,
+            syntheticArgumentClass,
+            syntheticInitializerConverterBuilder,
+            virtuallyMergedMethodsKeepInfos::add);
 
     SyntheticInitializerConverter syntheticInitializerConverter =
         syntheticInitializerConverterBuilder.build();
@@ -136,12 +148,44 @@
     // Record where the synthesized $r8$classId fields are read and written.
     if (mode.isInitial()) {
       createFieldAccessInfoCollectionModifier(groups).modify(appView.withLiveness());
+      transformIncompleteCode(groups, horizontalClassMergerGraphLens, executorService);
     } else {
       assert groups.stream().noneMatch(MergeGroup::hasClassIdField);
     }
 
     appView.pruneItems(
         prunedItems.toBuilder().setPrunedApp(appView.app()).build(), executorService);
+
+    amendKeepInfo(horizontalClassMergerGraphLens, virtuallyMergedMethodsKeepInfos);
+  }
+
+  private void amendKeepInfo(
+      HorizontalClassMergerGraphLens horizontalClassMergerGraphLens,
+      List<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfos) {
+    appView
+        .getKeepInfo()
+        .mutate(
+            keepInfo -> {
+              for (VirtuallyMergedMethodsKeepInfo virtuallyMergedMethodsKeepInfo :
+                  virtuallyMergedMethodsKeepInfos) {
+                DexMethod representative = virtuallyMergedMethodsKeepInfo.getRepresentative();
+                MethodLookupResult lookupResult =
+                    horizontalClassMergerGraphLens.lookupMethod(
+                        representative,
+                        null,
+                        Type.VIRTUAL,
+                        horizontalClassMergerGraphLens.getPrevious());
+                ProgramMethod mergedMethod =
+                    asProgramMethodOrNull(appView.definitionFor(lookupResult.getReference()));
+                if (mergedMethod != null) {
+                  keepInfo.joinMethod(
+                      mergedMethod,
+                      info -> info.merge(virtuallyMergedMethodsKeepInfo.getKeepInfo()));
+                  continue;
+                }
+                assert false;
+              }
+            });
   }
 
   private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
@@ -164,6 +208,32 @@
     return builder.build();
   }
 
+  private void transformIncompleteCode(
+      Collection<MergeGroup> groups,
+      HorizontalClassMergerGraphLens horizontalClassMergerGraphLens,
+      ExecutorService executorService)
+      throws ExecutionException {
+    ThreadUtils.processItems(
+        groups,
+        group -> {
+          if (group.hasClassIdField()) {
+            DexProgramClass target = group.getTarget();
+            target.forEachProgramVirtualMethodMatching(
+                definition ->
+                    definition.hasCode()
+                        && definition.getCode() instanceof IncompleteVirtuallyMergedMethodCode,
+                method -> {
+                  IncompleteVirtuallyMergedMethodCode code =
+                      (IncompleteVirtuallyMergedMethodCode) method.getDefinition().getCode();
+                  method
+                      .getDefinition()
+                      .setCode(code.toCfCode(method, horizontalClassMergerGraphLens), appView);
+                });
+          }
+        },
+        executorService);
+  }
+
   private DirectMappedDexApplication getNewApplication(HorizontallyMergedClasses mergedClasses) {
     // In the second round of class merging, we must forcefully remove the merged classes from the
     // application, since we won't run tree shaking before writing the application.
@@ -217,11 +287,15 @@
   private PrunedItems applyClassMergers(
       Collection<ClassMerger> classMergers,
       SyntheticArgumentClass syntheticArgumentClass,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
+      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder().setPrunedApp(appView.app());
     for (ClassMerger merger : classMergers) {
       merger.mergeGroup(
-          prunedItemsBuilder, syntheticArgumentClass, syntheticInitializerConverterBuilder);
+          prunedItemsBuilder,
+          syntheticArgumentClass,
+          syntheticInitializerConverterBuilder,
+          virtuallyMergedMethodsKeepInfoConsumer);
     }
     return prunedItemsBuilder.build();
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
index 839b6d5..ce6f6be 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerCfCode.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.horizontalclassmerging;
 
 import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfTryCatch;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexType;
 import java.util.List;
@@ -17,13 +16,8 @@
 public class HorizontalClassMergerCfCode extends CfCode {
 
   HorizontalClassMergerCfCode(
-      DexType originalHolder,
-      int maxStack,
-      int maxLocals,
-      List<CfInstruction> instructions,
-      List<CfTryCatch> tryCatchRanges,
-      List<LocalVariableInfo> localVariables) {
-    super(originalHolder, maxStack, maxLocals, instructions, tryCatchRanges, localVariables);
+      DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) {
+    super(originalHolder, maxStack, maxLocals, instructions);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 937a1fc..75743d6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -45,6 +45,11 @@
   }
 
   @Override
+  public boolean isHorizontalClassMergerGraphLens() {
+    return true;
+  }
+
+  @Override
   protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
     return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
new file mode 100644
index 0000000..65aed84
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
@@ -0,0 +1,244 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging;
+
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.cf.code.CfInstanceFieldRead;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfSwitch;
+import com.android.tools.r8.cf.code.CfSwitch.Kind;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.ClasspathMethod;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.IterableUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
+import it.unimi.dsi.fastutil.ints.IntBidirectionalIterator;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A short-lived piece of code that will be converted into {@link CfCode} using the method {@link
+ * #toCfCode(ProgramMethod, HorizontalClassMergerGraphLens)}.
+ */
+public class IncompleteVirtuallyMergedMethodCode extends Code {
+
+  private final DexField classIdField;
+  private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
+  private final DexMethod originalMethod;
+  private final DexMethod superMethod;
+
+  public IncompleteVirtuallyMergedMethodCode(
+      DexField classIdField,
+      Int2ReferenceSortedMap<DexMethod> mappedMethods,
+      DexMethod originalMethod,
+      DexMethod superMethod) {
+    this.mappedMethods = mappedMethods;
+    this.classIdField = classIdField;
+    this.superMethod = superMethod;
+    this.originalMethod = originalMethod;
+  }
+
+  @Override
+  public boolean isHorizontalClassMergingCode() {
+    return true;
+  }
+
+  /**
+   * Given a mapping from class ids to methods to invoke, this creates a piece of {@link CfCode} on
+   * the following form.
+   *
+   * <pre>
+   *   public Bar m(Foo foo) {
+   *     switch (this.classId) {
+   *       case 0:
+   *         return this.m1(foo);
+   *       case 1:
+   *         return this.m2(foo);
+   *       ...
+   *       default:
+   *         return this.mN(foo); // or super.m(foo);
+   *     }
+   *   }
+   * </pre>
+   *
+   * <p>Note that the methods to invoke must be rewritten using {@param lens}, since the invoked
+   * methods may be changed as a result of the horizontal class merger's fixup (e.g., if the method
+   * signature refers to a horizontally merged type).
+   */
+  public CfCode toCfCode(ProgramMethod method, HorizontalClassMergerGraphLens lens) {
+    // We store each argument in a local.
+    int maxLocals = 1 + IterableUtils.sumInt(method.getParameters(), DexType::getRequiredRegisters);
+
+    // We load all arguments on the stack and then the receiver to fetch the class id.
+    int maxStack = maxLocals + 1;
+
+    // Create instructions.
+    List<CfInstruction> instructions = new ArrayList<>();
+
+    // Setup keys and labels for switch.
+    IntBidirectionalIterator classIdIterator = mappedMethods.keySet().iterator();
+    int[] keys = new int[mappedMethods.size() - BooleanUtils.intValue(superMethod == null)];
+    List<CfLabel> labels = new ArrayList<>();
+    for (int key = 0; key < keys.length; key++) {
+      keys[key] = classIdIterator.nextInt();
+      labels.add(new CfLabel());
+    }
+    CfLabel fallthroughLabel = new CfLabel();
+
+    // Add instructions.
+    instructions.add(new CfLoad(ValueType.OBJECT, 0));
+    int localIndex = 1;
+    for (DexType parameter : method.getParameters()) {
+      instructions.add(new CfLoad(ValueType.fromDexType(parameter), localIndex));
+      localIndex += parameter.getRequiredRegisters();
+    }
+    instructions.add(new CfLoad(ValueType.OBJECT, 0));
+    instructions.add(new CfInstanceFieldRead(classIdField));
+
+    // Emit switch.
+    instructions.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, keys, labels));
+    for (int key = 0; key < keys.length; key++) {
+      int classId = keys[key];
+      DexMethod target = lens.getNextMethodSignature(mappedMethods.get(classId));
+      instructions.add(labels.get(key));
+      instructions.add(createCfFrameForSwitchCase(method, maxLocals));
+      instructions.add(
+          new CfInvoke(Opcodes.INVOKESPECIAL, target, method.getHolder().isInterface()));
+      if (method.getReturnType().isVoidType()) {
+        instructions.add(new CfReturnVoid());
+      } else {
+        instructions.add(new CfReturn(ValueType.fromDexType(method.getReturnType())));
+      }
+    }
+
+    // Emit fallthrough.
+    instructions.add(fallthroughLabel);
+    instructions.add(createCfFrameForSwitchCase(method, maxLocals));
+
+    DexMethod fallthroughTarget =
+        lens.getNextMethodSignature(
+            superMethod != null ? superMethod : mappedMethods.get(mappedMethods.lastIntKey()));
+    instructions.add(
+        new CfInvoke(Opcodes.INVOKESPECIAL, fallthroughTarget, method.getHolder().isInterface()));
+
+    // Emit return.
+    if (method.getReturnType().isVoidType()) {
+      instructions.add(new CfReturnVoid());
+    } else {
+      instructions.add(new CfReturn(ValueType.fromDexType(method.getReturnType())));
+    }
+    return new CfCode(originalMethod.getHolderType(), maxStack, maxLocals, instructions) {
+
+      @Override
+      public GraphLens getCodeLens(AppView<?> appView) {
+        GraphLens graphLens =
+            appView
+                .graphLens()
+                .asNonIdentityLens()
+                .find(GraphLens::isHorizontalClassMergerGraphLens);
+        assert graphLens != null;
+        return graphLens;
+      }
+    };
+  }
+
+  private static CfFrame createCfFrameForSwitchCase(ProgramMethod representative, int localsSize) {
+    Deque<FrameType> stack =
+        new ArrayDeque<>(representative.getDefinition().getNumberOfArguments());
+    for (int argumentIndex = 0;
+        argumentIndex < representative.getDefinition().getNumberOfArguments();
+        argumentIndex++) {
+      stack.add(FrameType.initialized(representative.getArgumentType(argumentIndex)));
+    }
+    return new CfFrame(createLocalFrames(representative, localsSize), stack);
+  }
+
+  private static Int2ReferenceAVLTreeMap<FrameType> createLocalFrames(
+      ProgramMethod representative, int localsSize) {
+    Int2ReferenceAVLTreeMap<FrameType> locals = new Int2ReferenceAVLTreeMap<>();
+    for (int argumentIndex = 0, localIndex = 0;
+        argumentIndex < representative.getDefinition().getNumberOfArguments();
+        argumentIndex++) {
+      FrameType frameType = FrameType.initialized(representative.getArgumentType(argumentIndex));
+      locals.put(localIndex++, frameType);
+      if (frameType.isWide()) {
+        locals.put(localIndex++, frameType);
+      }
+    }
+    assert locals.size() == localsSize;
+    return locals;
+  }
+
+  // Implement Code.
+
+  @Override
+  public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+    throw new Unreachable();
+  }
+
+  @Override
+  protected boolean computeEquals(Object other) {
+    throw new Unreachable();
+  }
+
+  @Override
+  protected int computeHashCode() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public int estimatedDexCodeSizeUpperBoundInBytes() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public boolean isEmptyVoidMethod() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public String toString() {
+    return "IncompleteVirtuallyMergedMethodCode";
+  }
+
+  @Override
+  public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+    return toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index 2affeaf..7aa753c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -167,12 +167,7 @@
     instructionBuilder.add(new CfReturnVoid());
 
     return new HorizontalClassMergerCfCode(
-        newMethodReference.getHolderType(),
-        maxStack.get(),
-        maxLocals,
-        instructionBuilder.build(),
-        ImmutableList.of(),
-        ImmutableList.of());
+        newMethodReference.getHolderType(), maxStack.get(), maxLocals, instructionBuilder.build());
   }
 
   private static void addCfInstructionsForInstanceFieldAssignments(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 4a3f2ff..0c36573 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -29,10 +29,10 @@
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.LinkedHashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -124,7 +124,7 @@
    * </ul>
    */
   public HorizontalClassMergerGraphLens fixupTypeReferences() {
-    List<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
+    Collection<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
     Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
     classes.forEach(this::fixupAttributes);
     classes.forEach(this::fixupProgramClassSuperTypes);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPoint.java
deleted file mode 100644
index 494e383..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPoint.java
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging;
-
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-import com.android.tools.r8.utils.IntBox;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Assuming a method signature <code>
- *   void method([args]);
- * </code>. This class generates code depending on which of the following cases it matches.
- *
- * <p>If the method does not override a method and is implemented by many (e.g. 2) classes:
- *
- * <pre>
- *   void method([args]) {
- *     switch (classId) {
- *       case 0:
- *         return method$1([args]);
- *       default:
- *         return method$2([args]);
- *     }
- *   }
- * </pre>
- *
- * <p>If the method overrides a method and is implemented by any number of classes:
- *
- * <pre>
- *   void method([args]) {
- *     switch (classId) {
- *       case 0:
- *         return method$1([args]);
- *       // ... further cases ...
- *       default:
- *         return super.method$1([args]);
- *     }
- *   }
- * </pre>
- */
-public class VirtualMethodEntryPoint extends SyntheticSourceCode {
-  private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
-  private final DexField classIdField;
-  private final DexMethod superMethod;
-
-  public VirtualMethodEntryPoint(
-      Int2ReferenceSortedMap<DexMethod> mappedMethods,
-      DexField classIdField,
-      DexMethod superMethod,
-      DexMethod newMethod,
-      Position callerPosition,
-      DexMethod originalMethod) {
-    super(newMethod.holder, newMethod, callerPosition, originalMethod);
-
-    assert classIdField != null;
-
-    this.mappedMethods = mappedMethods;
-    this.classIdField = classIdField;
-    this.superMethod = superMethod;
-  }
-
-  void addInvokeDirect(DexMethod method) {
-    add(
-        builder -> {
-          List<Value> arguments = new ArrayList<>(method.getArity() + 1);
-          arguments.add(builder.getReceiverValue());
-          if (builder.getArgumentValues() != null) {
-            arguments.addAll(builder.getArgumentValues());
-          }
-          builder.addInvoke(Type.DIRECT, method, method.proto, arguments, false);
-        });
-  }
-
-  void addInvokeSuper() {
-    assert superMethod != null;
-
-    add(
-        builder -> {
-          List<Value> arguments = new ArrayList<>(method.getArity() + 1);
-          arguments.add(builder.getReceiverValue());
-          if (builder.getArgumentValues() != null) {
-            arguments.addAll(builder.getArgumentValues());
-          }
-          builder.addInvoke(Type.SUPER, superMethod, superMethod.proto, arguments, false);
-        });
-  }
-
-  void handleReturn(int retRegister) {
-    if (proto.returnType.isVoidType()) {
-      add(IRBuilder::addReturn, endsBlock);
-    } else {
-      add(builder -> builder.addMoveResult(retRegister));
-      add(builder -> builder.addReturn(retRegister), endsBlock);
-    }
-  }
-
-  @Override
-  protected void prepareInstructions() {
-    int casesCount = mappedMethods.size();
-
-    // If there is no super method, use one of the cases as a fallthrough case.
-    if (superMethod == null) {
-      casesCount--;
-    }
-
-    assert casesCount > 0;
-
-    // Return value register if needed.
-    int returnRegister =
-        !proto.returnType.isVoidType() ? nextRegister(ValueType.fromDexType(proto.returnType)) : -1;
-
-    int[] keys = new int[casesCount];
-    int[] offsets = new int[casesCount];
-    IntBox fallthrough = new IntBox();
-
-    // Fetch the class id from the class id field.
-    int idRegister = nextRegister(ValueType.INT);
-    add(builder -> builder.addInstanceGet(idRegister, getReceiverRegister(), classIdField));
-
-    int switchIndex = lastInstructionIndex();
-    add(
-        builder -> builder.addSwitch(idRegister, keys, fallthrough.get(), offsets),
-        builder -> endsSwitch(builder, switchIndex, fallthrough.get(), offsets));
-
-    int index = 0;
-    for (Entry<DexMethod> entry : mappedMethods.int2ReferenceEntrySet()) {
-      int classId = entry.getIntKey();
-      DexMethod mappedMethod = entry.getValue();
-
-      // If there is no super method, then use the last case as the default case.
-      if (index >= casesCount) {
-        fallthrough.set(nextInstructionIndex());
-      } else {
-        keys[index] = classId;
-        offsets[index] = nextInstructionIndex();
-      }
-
-      addInvokeDirect(mappedMethod);
-      handleReturn(returnRegister);
-
-      index++;
-    }
-
-    // If the super class implements this method, then the fallthrough case should execute it.
-    if (superMethod != null) {
-      fallthrough.set(nextInstructionIndex());
-      addInvokeSuper();
-      handleReturn(returnRegister);
-    }
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index b18a69c..67cc625 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -10,13 +10,10 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.code.VirtualMethodEntryPointSynthesizedCode;
-import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.structural.Ordered;
@@ -26,6 +23,7 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class VirtualMethodMerger {
 
@@ -57,14 +55,13 @@
 
     /** Get the super method handle if this method overrides a parent method. */
     private DexMethod superMethod(
-        AppView<? extends AppInfoWithClassHierarchy> appView, DexProgramClass target) {
+        AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
       DexMethod template = methods.iterator().next().getReference();
       SingleResolutionResult resolutionResult =
           appView
               .appInfo()
-              .resolveMethodOnClass(template, target.getSuperType())
+              .resolveMethodOnClass(template, group.getSuperType())
               .asSingleResolution();
-
       if (resolutionResult == null || resolutionResult.getResolvedMethod().isAbstract()) {
         // If there is no super method or the method is abstract it should not be called.
         return null;
@@ -74,7 +71,7 @@
         return resolutionResult
             .getResolvedMethod()
             .getReference()
-            .withHolder(target.getSuperType(), appView.dexItemFactory());
+            .withHolder(group.getSuperType(), appView.dexItemFactory());
       }
       return resolutionResult.getResolvedMethod().getReference();
     }
@@ -82,8 +79,7 @@
     public VirtualMethodMerger build(
         AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
       // If not all the classes are in the merge group, find the fallback super method to call.
-      DexMethod superMethod =
-          methods.size() < group.size() ? superMethod(appView, group.getTarget()) : null;
+      DexMethod superMethod = methods.size() < group.size() ? superMethod(appView, group) : null;
       return new VirtualMethodMerger(appView, group, methods, superMethod);
     }
   }
@@ -229,7 +225,8 @@
   public void merge(
       ClassMethodsBuilder classMethodsBuilder,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Reference2IntMap<DexType> classIdentifiers) {
+      Reference2IntMap<DexType> classIdentifiers,
+      Consumer<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfoConsumer) {
     assert !methods.isEmpty();
 
     // Handle trivial merges.
@@ -252,7 +249,8 @@
       }
       DexMethod newMethod = moveMethod(classMethodsBuilder, method);
       lensBuilder.recordNewMethodSignature(method.getReference(), newMethod);
-      classIdToMethodMap.put(classIdentifiers.getInt(method.getHolderType()), newMethod);
+      classIdToMethodMap.put(
+          classIdentifiers.getInt(method.getHolderType()), method.getReference());
       if (representative == null) {
         representative = method;
       }
@@ -271,14 +269,9 @@
             classMethodsBuilder::isFresh);
     DexEncodedMethod representativeMethod = representative.getDefinition();
     DexMethod newMethodReference = getNewMethodReference();
-    AbstractSynthesizedCode synthesizedCode =
-        new VirtualMethodEntryPointSynthesizedCode(
-            classIdToMethodMap,
-            group.getClassIdField(),
-            superMethod,
-            newMethodReference,
-            bridgeMethodReference,
-            appView.dexItemFactory());
+    IncompleteVirtuallyMergedMethodCode synthesizedCode =
+        new IncompleteVirtuallyMergedMethodCode(
+            group.getClassIdField(), classIdToMethodMap, originalMethodReference, superMethod);
     DexEncodedMethod newMethod =
         DexEncodedMethod.syntheticBuilder()
             .setMethod(newMethodReference)
@@ -293,13 +286,20 @@
     }
 
     // Map each old non-abstract method to the newly synthesized method in the graph lens.
+    VirtuallyMergedMethodsKeepInfo virtuallyMergedMethodsKeepInfo =
+        new VirtuallyMergedMethodsKeepInfo(representative.getReference());
     for (ProgramMethod oldMethod : methods) {
       lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference);
+      virtuallyMergedMethodsKeepInfo.amendKeepInfo(appView.getKeepInfo(oldMethod));
     }
 
     // Add a mapping from a synthetic name to the synthetic merged method.
     lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference);
 
     classMethodsBuilder.addVirtualMethod(newMethod);
+
+    if (!virtuallyMergedMethodsKeepInfo.getKeepInfo().isBottom()) {
+      virtuallyMergedMethodsKeepInfoConsumer.accept(virtuallyMergedMethodsKeepInfo);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtuallyMergedMethodsKeepInfo.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtuallyMergedMethodsKeepInfo.java
new file mode 100644
index 0000000..b395d5d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtuallyMergedMethodsKeepInfo.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
+
+public class VirtuallyMergedMethodsKeepInfo {
+
+  private final DexMethod representative;
+  private final KeepMethodInfo.Joiner keepInfo = KeepMethodInfo.newEmptyJoiner();
+
+  public VirtuallyMergedMethodsKeepInfo(DexMethod representative) {
+    this.representative = representative;
+  }
+
+  public void amendKeepInfo(KeepMethodInfo keepInfo) {
+    this.keepInfo.merge(keepInfo.joiner());
+  }
+
+  public DexMethod getRepresentative() {
+    return representative;
+  }
+
+  public Joiner getKeepInfo() {
+    return keepInfo;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
deleted file mode 100644
index 7cfd3dc..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.horizontalclassmerging.code;
-
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.horizontalclassmerging.VirtualMethodEntryPoint;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
-import java.util.function.Consumer;
-
-public class VirtualMethodEntryPointSynthesizedCode extends SynthesizedCode {
-
-  private final DexItemFactory dexItemFactory;
-  private final Int2ReferenceSortedMap<DexMethod> mappedMethods;
-  private final DexMethod superMethod;
-
-  public VirtualMethodEntryPointSynthesizedCode(
-      Int2ReferenceSortedMap<DexMethod> mappedMethods,
-      DexField classIdField,
-      DexMethod superMethod,
-      DexMethod method,
-      DexMethod originalMethod,
-      DexItemFactory factory) {
-    super(
-        (context, position) ->
-            new VirtualMethodEntryPoint(
-                mappedMethods,
-                classIdField,
-                computeSuperMethodTarget(superMethod, context, factory),
-                method,
-                position,
-                originalMethod));
-    this.dexItemFactory = factory;
-    this.mappedMethods = mappedMethods;
-    this.superMethod = superMethod;
-  }
-
-  private static DexMethod computeSuperMethodTarget(
-      DexMethod superMethod, ProgramMethod method, DexItemFactory factory) {
-    // We are only using superMethod as a bit but if this is changed to generate CfCode directly,
-    // the superMethod needs to be computed by mapping through the lens.
-    if (superMethod == null) {
-      return null;
-    }
-    return method.getReference().withHolder(method.getHolder().superType, factory);
-  }
-
-  @Override
-  public Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method) {
-    return registry -> registerReachableDefinitions(method, registry);
-  }
-
-  private void registerReachableDefinitions(DexClassAndMethod method, UseRegistry registry) {
-    assert registry.getTraversalContinuation().shouldContinue();
-    for (DexMethod mappedMethod : mappedMethods.values()) {
-      registry.registerInvokeDirect(mappedMethod);
-      if (registry.getTraversalContinuation().shouldBreak()) {
-        return;
-      }
-    }
-    DexMethod superMethodTarget =
-        computeSuperMethodTarget(superMethod, method.asProgramMethod(), dexItemFactory);
-    if (superMethodTarget != null) {
-      registry.registerInvokeSuper(superMethodTarget);
-    }
-  }
-
-  @Override
-  public boolean isHorizontalClassMergingCode() {
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 62b8e28..b3976c7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -39,6 +39,7 @@
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.android.tools.r8.shaking.InstantiationReason;
+import com.android.tools.r8.shaking.KeepMethodInfo;
 import com.android.tools.r8.shaking.KeepReason;
 import com.android.tools.r8.utils.BitUtils;
 import com.android.tools.r8.utils.OptionalBool;
@@ -133,8 +134,13 @@
    */
   @Override
   public void processNewlyLiveMethod(
-      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
+      ProgramMethod method,
+      ProgramDefinition context,
+      Enqueuer enqueuer,
+      EnqueuerWorklist worklist) {
     if (references.isFindLiteExtensionByNumberMethod(method.getReference())) {
+      enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
+          method, KeepMethodInfo.newEmptyJoiner().disallowParameterReordering());
       findLiteExtensionByNumberMethods.add(method);
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index fa59829..fe2c785 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -62,7 +63,7 @@
   private void internalConvertClasses(
       ClassConverterResult.Builder resultBuilder, ExecutorService executorService)
       throws ExecutionException {
-    List<DexProgramClass> classes = appView.appInfo().classes();
+    Collection<DexProgramClass> classes = appView.appInfo().classes();
 
       CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
           new CfClassSynthesizerDesugaringEventConsumer();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 29bee42..4d1cfc2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -435,7 +435,7 @@
     return new IRBuilder(
         method,
         appView,
-        appView.codeLens(),
+        method.getDefinition().getCode().getCodeLens(appView),
         source,
         origin,
         lookupPrototypeChanges(appView, method),
@@ -459,14 +459,6 @@
     return appView.graphLens().lookupPrototypeChangesForMethodDefinition(method.getReference());
   }
 
-  public static RewrittenPrototypeDescription lookupPrototypeChangesForInlinee(
-      AppView<?> appView, ProgramMethod method, MethodProcessor methodProcessor) {
-    if (methodProcessor.shouldApplyCodeRewritings(method)) {
-      return appView.graphLens().lookupPrototypeChangesForMethodDefinition(method.getReference());
-    }
-    return RewrittenPrototypeDescription.none();
-  }
-
   private IRBuilder(
       ProgramMethod method,
       AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index ba22a02..613ab36 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -969,7 +969,7 @@
 
   private Deque<GraphLensInterval> getUnappliedLenses(ProgramMethod method) {
     Deque<GraphLensInterval> unappliedLenses = new ArrayDeque<>(8);
-    GraphLens codeLens = appView.codeLens();
+    GraphLens codeLens = method.getDefinition().getCode().getCodeLens(appView);
     GraphLens currentLens = appView.graphLens();
     DexMethod currentMethod = method.getReference();
     while (currentLens != codeLens) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index a46a6bc..125b53c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -8,6 +8,8 @@
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.classhierarchy.MethodOverridesCollector;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.Sets;
@@ -57,14 +59,17 @@
     private final Set<DexMethod> multiCallerInlineCandidates = Sets.newIdentityHashSet();
 
     CallGraphBasedCallSiteInformation(AppView<AppInfoWithLiveness> appView, CallGraph graph) {
+      InternalOptions options = appView.options();
       ProgramMethodSet pinned =
           MethodOverridesCollector.findAllMethodsAndOverridesThatMatches(
               appView,
               ImmediateProgramSubtypingInfo.create(appView),
               appView.appInfo().classes(),
-              method ->
-                  appView.getKeepInfo(method).isPinned(appView.options())
-                      || appView.appInfo().isMethodTargetedByInvokeDynamic(method));
+              method -> {
+                KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+                return !keepInfo.isClosedWorldReasoningAllowed(options)
+                    || keepInfo.isPinned(options);
+              });
 
       for (Node node : graph.getNodes()) {
         ProgramMethod method = node.getProgramMethod();
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 dd2bb76..68d48dc 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
@@ -179,6 +179,9 @@
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Sv2)) {
         initializeAndroidSv2MethodProviders(factory);
       }
+      if (options.getMinApiLevel().isLessThan(AndroidApiLevel.T)) {
+        initializeAndroidTMethodProviders(factory);
+      }
 
       // The following providers are implemented at API level T. For backporting they require
       // the java.util.Optional class to be present, either through library desugaring or natively.
@@ -1158,6 +1161,145 @@
       }
     }
 
+    private void initializeAndroidTMethodProviders(DexItemFactory factory) {
+      // java.lang.Integer.
+      {
+        // int Integer.parseInt(CharSequence s, int beginIndex, int endIndex, int radix)
+        DexType type = factory.boxedIntType;
+        DexString name = factory.createString("parseInt");
+        DexProto proto =
+            factory.createProto(
+                factory.intType,
+                factory.charSequenceType,
+                factory.intType,
+                factory.intType,
+                factory.intType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            appView.options().canParseNumbersWithPlusPrefix()
+                ? new MethodGenerator(
+                    method,
+                    BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadix,
+                    "parseIntSubsequenceWithRadix")
+                : new MethodGenerator(
+                    method,
+                    BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadixDalvik,
+                    "parseIntSubsequenceWithRadix"));
+      }
+      {
+        // int Integer.parseUnsignedInt(CharSequence s, int beginIndex, int endIndex, int radix)
+        DexType type = factory.boxedIntType;
+        DexString name = factory.createString("parseUnsignedInt");
+        DexProto proto =
+            factory.createProto(
+                factory.intType,
+                factory.charSequenceType,
+                factory.intType,
+                factory.intType,
+                factory.intType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new MethodGenerator(
+                method,
+                BackportedMethods::IntegerMethods_parseUnsignedIntSubsequenceWithRadix,
+                "parseIntSubsequenceWithRadix"));
+      }
+
+      // java.lang.Long.
+      {
+        // long Long.parseLong(CharSequence s, int beginIndex, int endIndex, int radix)
+        DexType type = factory.boxedLongType;
+        DexString name = factory.createString("parseLong");
+        DexProto proto =
+            factory.createProto(
+                factory.longType,
+                factory.charSequenceType,
+                factory.intType,
+                factory.intType,
+                factory.intType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            appView.options().canParseNumbersWithPlusPrefix()
+                ? new MethodGenerator(
+                    method,
+                    BackportedMethods::LongMethods_parseLongSubsequenceWithRadix,
+                    "parseLongSubsequenceWithRadix")
+                : new MethodGenerator(
+                    method,
+                    BackportedMethods::LongMethods_parseLongSubsequenceWithRadixDalvik,
+                    "parseLongSubsequenceWithRadix"));
+      }
+      {
+        // long Long.parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix)
+        DexType type = factory.boxedLongType;
+        DexString name = factory.createString("parseUnsignedLong");
+        DexProto proto =
+            factory.createProto(
+                factory.longType,
+                factory.charSequenceType,
+                factory.intType,
+                factory.intType,
+                factory.intType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new MethodGenerator(
+                method,
+                BackportedMethods::LongMethods_parseUnsignedLongSubsequenceWithRadix,
+                "parseUnsignedLongSubsequenceWithRadix"));
+      }
+      // java.lang.String.
+      {
+        // String String.repeat(int)
+        DexType type = factory.stringType;
+        DexString name = factory.createString("repeat");
+        DexProto proto = factory.createProto(factory.stringType, factory.intType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method, BackportedMethods::StringMethods_repeat, "repeat", type));
+      }
+      {
+        // boolean String.isBlank()
+        DexType type = factory.stringType;
+        DexString name = factory.createString("isBlank");
+        DexProto proto = factory.createProto(factory.booleanType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method, BackportedMethods::StringMethods_isBlank, "isBlank", type));
+      }
+      {
+        // String String.strip()
+        DexType type = factory.stringType;
+        DexString name = factory.createString("strip");
+        DexProto proto = factory.createProto(factory.stringType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method, BackportedMethods::StringMethods_strip, "strip", type));
+      }
+      {
+        // String String.stripLeading()
+        DexType type = factory.stringType;
+        DexString name = factory.createString("stripLeading");
+        DexProto proto = factory.createProto(factory.stringType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method, BackportedMethods::StringMethods_stripLeading, "stripLeading", type));
+      }
+      {
+        // String String.stripTrailing()
+        DexType type = factory.stringType;
+        DexString name = factory.createString("stripTrailing");
+        DexProto proto = factory.createProto(factory.stringType);
+        DexMethod method = factory.createMethod(type, proto, name);
+        addProvider(
+            new StatifyingMethodGenerator(
+                method, BackportedMethods::StringMethods_stripTrailing, "stripTrailing", type));
+      }
+    }
+
     private void initializeAndroidOptionalTMethodProviders(DexItemFactory factory) {
       DexType optionalType = factory.optionalType;
       DexType[] optionalTypes =
@@ -1275,67 +1417,7 @@
     }
 
     private void initializeJava9MethodProviders(DexItemFactory factory) {
-      // Integer
-      DexType type = factory.boxedIntType;
-      // int Integer.parseInt(CharSequence s, int beginIndex, int endIndex, int radix)
-      DexString name = factory.createString("parseInt");
-      DexProto proto =
-          factory.createProto(
-              factory.intType,
-              factory.charSequenceType,
-              factory.intType,
-              factory.intType,
-              factory.intType);
-      DexMethod method = factory.createMethod(type, proto, name);
-      addProvider(
-          appView.options().canParseNumbersWithPlusPrefix()
-              ? new MethodGenerator(
-                  method,
-                  BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadix,
-                  "parseIntSubsequenceWithRadix")
-              : new MethodGenerator(
-                  method,
-                  BackportedMethods::IntegerMethods_parseIntSubsequenceWithRadixDalvik,
-                  "parseIntSubsequenceWithRadix"));
-
-      // Long
-      type = factory.boxedLongType;
-      // long Long.parseLong(CharSequence s, int beginIndex, int endIndex, int radix)
-      name = factory.createString("parseLong");
-      proto =
-          factory.createProto(
-              factory.longType,
-              factory.charSequenceType,
-              factory.intType,
-              factory.intType,
-              factory.intType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          appView.options().canParseNumbersWithPlusPrefix()
-              ? new MethodGenerator(
-                  method,
-                  BackportedMethods::LongMethods_parseLongSubsequenceWithRadix,
-                  "parseLongSubsequenceWithRadix")
-              : new MethodGenerator(
-                  method,
-                  BackportedMethods::LongMethods_parseLongSubsequenceWithRadixDalvik,
-                  "parseLongSubsequenceWithRadix"));
-
-      // long Long.parseUnsignedLong(CharSequence s, int beginIndex, int endIndex, int radix)
-      name = factory.createString("parseUnsignedLong");
-      proto =
-          factory.createProto(
-              factory.longType,
-              factory.charSequenceType,
-              factory.intType,
-              factory.intType,
-              factory.intType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new MethodGenerator(
-              method,
-              BackportedMethods::LongMethods_parseUnsignedLongSubsequenceWithRadix,
-              "parseUnsignedLongSubsequenceWithRadix"));
+      // Nothing right now.
     }
 
     private void initializeJava10MethodProviders(DexItemFactory factory) {
@@ -1364,49 +1446,6 @@
       method = factory.createMethod(type, proto, name);
       addProvider(
           new MethodGenerator(method, BackportedMethods::CharSequenceMethods_compare, "compare"));
-
-      // String
-      type = factory.stringType;
-
-      // String String.repeat(int)
-      name = factory.createString("repeat");
-      proto = factory.createProto(factory.stringType, factory.intType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method, BackportedMethods::StringMethods_repeat, "repeat", type));
-
-      // boolean String.isBlank()
-      name = factory.createString("isBlank");
-      proto = factory.createProto(factory.booleanType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method, BackportedMethods::StringMethods_isBlank, "isBlank", type));
-
-      // String String.strip()
-      name = factory.createString("strip");
-      proto = factory.createProto(factory.stringType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method, BackportedMethods::StringMethods_strip, "strip", type));
-
-      // String String.stripLeading()
-      name = factory.createString("stripLeading");
-      proto = factory.createProto(factory.stringType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method, BackportedMethods::StringMethods_stripLeading, "stripLeading", type));
-
-      // String String.stripTrailing()
-      name = factory.createString("stripTrailing");
-      proto = factory.createProto(factory.stringType);
-      method = factory.createMethod(type, proto, name);
-      addProvider(
-          new StatifyingMethodGenerator(
-              method, BackportedMethods::StringMethods_stripTrailing, "stripTrailing", type));
     }
 
     private void initializeStreamMethodProviders(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 ccd00a9..1b2332b 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
@@ -2597,6 +2597,53 @@
         ImmutableList.of());
   }
 
+  public static CfCode IntegerMethods_parseUnsignedIntSubsequenceWithRadix(
+      InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        3,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.INT, 1),
+            new CfLoad(ValueType.INT, 2),
+            new CfInvoke(
+                185,
+                options.itemFactory.createMethod(
+                    options.itemFactory.charSequenceType,
+                    options.itemFactory.createProto(
+                        options.itemFactory.charSequenceType,
+                        options.itemFactory.intType,
+                        options.itemFactory.intType),
+                    options.itemFactory.createString("subSequence")),
+                true),
+            new CfInvoke(
+                185,
+                options.itemFactory.createMethod(
+                    options.itemFactory.charSequenceType,
+                    options.itemFactory.createProto(options.itemFactory.stringType),
+                    options.itemFactory.createString("toString")),
+                true),
+            new CfLoad(ValueType.INT, 3),
+            new CfInvoke(
+                184,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Ljava/lang/Integer;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.intType,
+                        options.itemFactory.stringType,
+                        options.itemFactory.intType),
+                    options.itemFactory.createString("parseUnsignedInt")),
+                false),
+            new CfReturn(ValueType.INT),
+            label1),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
   public static CfCode IntegerMethods_parseUnsignedIntWithRadix(
       InternalOptions options, DexMethod method) {
     CfLabel label0 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
new file mode 100644
index 0000000..bd146e9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAmender.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import java.util.Map;
+
+/**
+ * The LibraryAmender is responsible in amending the library so that desugared library can be
+ * applied. For example, it can insert missing methods which are not present in the library but are
+ * supported in desugared library.
+ */
+public class DesugaredLibraryAmender {
+
+  private final AppView<?> appView;
+
+  public static void run(AppView<?> appView) {
+    run(appView, appView.options().machineDesugaredLibrarySpecification.getAmendLibraryMethods());
+  }
+
+  public static void run(AppView<?> appView, Map<DexMethod, MethodAccessFlags> amendLibrary) {
+    if (amendLibrary.isEmpty()) {
+      return;
+    }
+    new DesugaredLibraryAmender(appView).run(amendLibrary);
+  }
+
+  private DesugaredLibraryAmender(AppView<?> appView) {
+    this.appView = appView;
+  }
+
+  private void run(Map<DexMethod, MethodAccessFlags> amendLibrary) {
+    amendLibrary.forEach(this::amendLibraryMethod);
+  }
+
+  private void amendLibraryMethod(DexMethod method, MethodAccessFlags methodAccessFlags) {
+    DexClass dexClass = appView.contextIndependentDefinitionFor(method.getHolderType());
+    if (dexClass == null || !dexClass.isLibraryClass()) {
+      // Consider just throwing an error.
+      appView
+          .options()
+          .reporter
+          .warning(
+              "Desugared library: Cannot amend library method "
+                  + method
+                  + " because the holder is not a library class"
+                  + (dexClass == null ? "(null)." : "."));
+      return;
+    }
+    if (dexClass.lookupMethod(method) != null) {
+      return;
+    }
+    DexEncodedMethod encodedMethod =
+        DexEncodedMethod.syntheticBuilder()
+            .setMethod(method)
+            .setAccessFlags(methodAccessFlags)
+            .setCode(null)
+            .setApiLevelForDefinition(appView.computedMinApiLevel())
+            .build();
+    dexClass.getMethodCollection().addMethod(encodedMethod);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
new file mode 100644
index 0000000..f96e5cd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+public interface DesugaredLibrarySpecification {
+
+  default boolean isHuman() {
+    return false;
+  }
+
+  default boolean isLegacy() {
+    return false;
+  }
+
+  default LegacyDesugaredLibrarySpecification asLegacyDesugaredLibrarySpecification() {
+    return null;
+  }
+
+  default HumanDesugaredLibrarySpecification asHumanDesugaredLibrarySpecification() {
+    return null;
+  }
+
+  boolean isEmpty();
+
+  boolean isLibraryCompilation();
+
+  String getJsonSource();
+
+  String getSynthesizedLibraryClassesPackagePrefix();
+
+  List<String> getExtraKeepRules();
+
+  AndroidApiLevel getRequiredCompilationApiLevel();
+
+  MachineDesugaredLibrarySpecification toMachineSpecification(
+      InternalOptions options, AndroidApp app) throws IOException;
+
+  MachineDesugaredLibrarySpecification toMachineSpecification(
+      InternalOptions options, Path library, Path desugaredJDKLib) throws IOException;
+
+  default MachineDesugaredLibrarySpecification toMachineSpecification(
+      InternalOptions options, Path library) throws IOException {
+    assert !isLibraryCompilation();
+    return toMachineSpecification(options, library, null);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
new file mode 100644
index 0000000..83ee3ec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecificationParser.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.util.function.Consumer;
+
+public class DesugaredLibrarySpecificationParser {
+
+  public static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version";
+  private static final int MIN_HUMAN_CONFIGURATION_FORMAT_VERSION = 100;
+
+  public static DesugaredLibrarySpecification parseDesugaredLibrarySpecification(
+      StringResource stringResource,
+      DexItemFactory dexItemFactory,
+      Reporter reporter,
+      boolean libraryCompilation,
+      int minAPILevel) {
+    Origin origin = stringResource.getOrigin();
+    assert origin != null;
+    String jsonConfigString;
+    JsonObject jsonConfig;
+    try {
+      jsonConfigString = stringResource.getString();
+      JsonParser parser = new JsonParser();
+      jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
+    } catch (Exception e) {
+      throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
+    }
+
+    if (isHumanSpecification(jsonConfig, reporter, origin)) {
+      return new HumanDesugaredLibrarySpecificationParser(
+              dexItemFactory, reporter, libraryCompilation, minAPILevel)
+          .parse(origin, jsonConfigString, jsonConfig);
+    }
+    return new LegacyDesugaredLibrarySpecificationParser(
+            dexItemFactory, reporter, libraryCompilation, minAPILevel)
+        .parse(origin, jsonConfigString, jsonConfig);
+  }
+
+  public static DesugaredLibrarySpecification parseDesugaredLibrarySpecificationforTesting(
+      StringResource stringResource,
+      DexItemFactory dexItemFactory,
+      Reporter reporter,
+      boolean libraryCompilation,
+      int minAPILevel,
+      Consumer<TopLevelFlagsBuilder<?>> topLevelFlagsAmender) {
+    Origin origin = stringResource.getOrigin();
+    assert origin != null;
+    String jsonConfigString;
+    JsonObject jsonConfig;
+    try {
+      jsonConfigString = stringResource.getString();
+      JsonParser parser = new JsonParser();
+      jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
+    } catch (Exception e) {
+      throw reporter.fatalError(new ExceptionDiagnostic(e, origin));
+    }
+    if (isHumanSpecification(jsonConfig, reporter, origin)) {
+      return new HumanDesugaredLibrarySpecificationParser(
+              dexItemFactory, reporter, libraryCompilation, minAPILevel)
+          .parse(origin, jsonConfigString, jsonConfig, topLevelFlagsAmender);
+    }
+    return new LegacyDesugaredLibrarySpecificationParser(
+            dexItemFactory, reporter, libraryCompilation, minAPILevel)
+        .parse(origin, jsonConfigString, jsonConfig, topLevelFlagsAmender);
+  }
+
+  public static boolean isHumanSpecification(
+      JsonObject jsonConfig, Reporter reporter, Origin origin) {
+    if (!jsonConfig.has(CONFIGURATION_FORMAT_VERSION_KEY)) {
+      throw reporter.fatalError(
+          new StringDiagnostic(
+              "Invalid desugared library configuration. Expected required key '"
+                  + CONFIGURATION_FORMAT_VERSION_KEY
+                  + "'",
+              origin));
+    }
+
+    return jsonConfig.get(CONFIGURATION_FORMAT_VERSION_KEY).getAsInt()
+        >= MIN_HUMAN_CONFIGURATION_FORMAT_VERSION;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/TopLevelFlagsBuilder.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/TopLevelFlagsBuilder.java
new file mode 100644
index 0000000..e7c7bb5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/TopLevelFlagsBuilder.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
+
+public interface TopLevelFlagsBuilder<T extends TopLevelFlagsBuilder<?>> {
+
+  T setSupportAllCallbacksFromLibrary(boolean supportAllCallbacksFromLibrary);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java
new file mode 100644
index 0000000..d63efb6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/AbstractMethodParser.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+/** Parse methods of the form: modifiers* returnType holder#name(arg0, ..., argN) */
+public abstract class AbstractMethodParser {
+
+  private static final String SEPARATORS = "\\s+|,\\s+|#|\\(|\\)";
+
+  private static final Map<String, Integer> modifiers =
+      ImmutableMap.<String, Integer>builder()
+          .put("public", Constants.ACC_PUBLIC)
+          .put("private", Constants.ACC_PRIVATE)
+          .put("protected", Constants.ACC_PROTECTED)
+          .put("final", Constants.ACC_FINAL)
+          .put("abstract", Constants.ACC_ABSTRACT)
+          .put("static", Constants.ACC_STATIC)
+          .build();
+
+  final DexItemFactory factory;
+
+  protected AbstractMethodParser(DexItemFactory factory) {
+    this.factory = factory;
+  }
+
+  // TODO(b/218755060): It would be nice to avoid the split regexp and use a nextToken()
+  //  method instead, then add a TraversalContinuation.
+  public void parseMethod(String signature) {
+    String[] tokens = signature.split(SEPARATORS);
+    if (tokens.length < 3) {
+      throw new CompilationError("Desugared library: cannot parse method " + signature);
+    }
+    methodStart();
+    int first = parseModifiers(tokens);
+    returnType(stringTypeToDexType(tokens[first]));
+    holderType(stringTypeToDexType(tokens[first + 1]));
+    methodName(factory.createString(tokens[first + 1 + 1]));
+    for (int i = first + 3; i < tokens.length; i++) {
+      argType(stringTypeToDexType(tokens[i]));
+    }
+    methodEnd();
+  }
+
+  private DexType stringTypeToDexType(String stringType) {
+    return factory.createType(DescriptorUtils.javaTypeToDescriptor(stringType));
+  }
+
+  private int parseModifiers(String[] split) {
+    int index = 0;
+    while (modifiers.containsKey(split[index])) {
+      modifier(modifiers.get(split[index]));
+      index++;
+    }
+    return index;
+  }
+
+  protected abstract void methodStart();
+
+  protected abstract void methodEnd();
+
+  protected abstract void returnType(DexType type);
+
+  protected abstract void argType(DexType type);
+
+  protected abstract void modifier(int access);
+
+  protected abstract void holderType(DexType type);
+
+  protected abstract void methodName(DexString name);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index 1e871b3..157dcc6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -8,12 +8,19 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Path;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-public class HumanDesugaredLibrarySpecification {
+public class HumanDesugaredLibrarySpecification implements DesugaredLibrarySpecification {
 
   private final boolean libraryCompilation;
   private final HumanTopLevelFlags topLevelFlags;
@@ -28,18 +35,36 @@
     this.rewritingFlags = rewritingFlags;
   }
 
+  @Override
+  public boolean isEmpty() {
+    return rewritingFlags.isEmpty();
+  }
+
+  @Override
+  public boolean isHuman() {
+    return true;
+  }
+
+  @Override
+  public HumanDesugaredLibrarySpecification asHumanDesugaredLibrarySpecification() {
+    return this;
+  }
+
   public boolean supportAllCallbacksFromLibrary() {
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
+  @Override
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
 
+  @Override
   public boolean isLibraryCompilation() {
     return libraryCompilation;
   }
 
+  @Override
   public String getSynthesizedLibraryClassesPackagePrefix() {
     return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
   }
@@ -65,12 +90,12 @@
   }
 
   public Map<DexType, DexType> getEmulateLibraryInterface() {
-    return rewritingFlags.getEmulateLibraryInterface();
+    return rewritingFlags.getEmulatedInterfaces();
   }
 
   // If the method is retargeted, answers the retargeted method, else null.
   public DexMethod retargetMethod(DexEncodedMethod method, AppView<?> appView) {
-    Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetCoreLibMember();
+    Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetMethod();
     DexType dexType = retargetCoreLibMember.get(method.getReference());
     if (dexType != null) {
       return appView
@@ -88,11 +113,11 @@
   }
 
   public Map<DexMethod, DexType> getRetargetCoreLibMember() {
-    return rewritingFlags.getRetargetCoreLibMember();
+    return rewritingFlags.getRetargetMethod();
   }
 
   public Map<DexType, DexType> getBackportCoreLibraryMember() {
-    return rewritingFlags.getBackportCoreLibraryMember();
+    return rewritingFlags.getLegacyBackport();
   }
 
   public Map<DexType, DexType> getCustomConversions() {
@@ -108,18 +133,38 @@
   }
 
   public Set<DexType> getDontRetargetLibMember() {
-    return rewritingFlags.getDontRetargetLibMember();
+    return rewritingFlags.getDontRetarget();
   }
 
+  @Override
   public List<String> getExtraKeepRules() {
     return topLevelFlags.getExtraKeepRules();
   }
 
+  @Override
   public String getJsonSource() {
     return topLevelFlags.getJsonSource();
   }
 
   public boolean isEmptyConfiguration() {
-    return false;
+    return rewritingFlags.isEmpty();
+  }
+
+  @Override
+  public MachineDesugaredLibrarySpecification toMachineSpecification(
+      InternalOptions options, AndroidApp app) throws IOException {
+    return new HumanToMachineSpecificationConverter()
+        .convert(
+            this,
+            isLibraryCompilation() ? app.getProgramResourceProviders() : null,
+            app.getLibraryResourceProviders(),
+            options);
+  }
+
+  @Override
+  public MachineDesugaredLibrarySpecification toMachineSpecification(
+      InternalOptions options, Path library, Path desugaredJDKLib) throws IOException {
+    return new HumanToMachineSpecificationConverter()
+        .convert(this, isLibraryCompilation() ? desugaredJDKLib : null, library, options);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index e01def6..bb4bc68 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -4,10 +4,17 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isHumanSpecification;
+
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.TopLevelFlagsBuilder;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -18,10 +25,13 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.function.Consumer;
 
 public class HumanDesugaredLibrarySpecificationParser {
 
+  public static final int CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION = 100;
+
   static final String IDENTIFIER_KEY = "identifier";
   static final String REQUIRED_COMPILATION_API_LEVEL_KEY = "required_compilation_api_level";
   static final String SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY =
@@ -35,15 +45,18 @@
   static final String WRAPPER_CONVERSION_KEY = "wrapper_conversion";
   static final String CUSTOM_CONVERSION_KEY = "custom_conversion";
   static final String REWRITE_PREFIX_KEY = "rewrite_prefix";
-  static final String RETARGET_LIB_MEMBER_KEY = "retarget_lib_member";
+  static final String RETARGET_METHOD_KEY = "retarget_method";
+  static final String REWRITE_DERIVED_PREFIX_KEY = "rewrite_derived_prefix";
   static final String EMULATE_INTERFACE_KEY = "emulate_interface";
   static final String DONT_REWRITE_KEY = "dont_rewrite";
-  static final String DONT_RETARGET_LIB_MEMBER_KEY = "dont_retarget_lib_member";
+  static final String DONT_RETARGET_KEY = "dont_retarget";
   static final String BACKPORT_KEY = "backport";
+  static final String AMEND_LIBRARY_METHOD_KEY = "amend_library_method";
   static final String SHRINKER_CONFIG_KEY = "shrinker_config";
   static final String SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY = "support_all_callbacks_from_library";
 
   private final DexItemFactory dexItemFactory;
+  private final HumanMethodParser methodParser;
   private final Reporter reporter;
   private final boolean libraryCompilation;
   private final int minAPILevel;
@@ -57,6 +70,7 @@
       boolean libraryCompilation,
       int minAPILevel) {
     this.dexItemFactory = dexItemFactory;
+    this.methodParser = new HumanMethodParser(dexItemFactory);
     this.reporter = reporter;
     this.minAPILevel = minAPILevel;
     this.libraryCompilation = libraryCompilation;
@@ -90,13 +104,26 @@
   }
 
   public HumanDesugaredLibrarySpecification parse(StringResource stringResource) {
-    return parse(stringResource, builder -> {});
+    String jsonConfigString = parseJson(stringResource);
+    return parse(origin, jsonConfigString, jsonConfig, ignored -> {});
   }
 
   public HumanDesugaredLibrarySpecification parse(
-      StringResource stringResource, Consumer<HumanTopLevelFlags.Builder> topLevelFlagAmender) {
-    String jsonConfigString = parseJson(stringResource);
+      Origin origin, String jsonConfigString, JsonObject jsonConfig) {
+    return parse(origin, jsonConfigString, jsonConfig, ignored -> {});
+  }
 
+  public HumanDesugaredLibrarySpecification parse(
+      Origin origin,
+      String jsonConfigString,
+      JsonObject jsonConfig,
+      Consumer<TopLevelFlagsBuilder<?>> topLevelFlagAmender) {
+    if (!isHumanSpecification(jsonConfig, reporter, origin)) {
+      reporter.error(
+          "Attempt to parse a non desugared library human specification as a human specification.");
+    }
+    this.origin = origin;
+    this.jsonConfig = jsonConfig;
     HumanTopLevelFlags topLevelFlags = parseTopLevelFlags(jsonConfigString, topLevelFlagAmender);
 
     HumanRewritingFlags legacyRewritingFlags = parseRewritingFlags();
@@ -104,7 +131,7 @@
     HumanDesugaredLibrarySpecification config =
         new HumanDesugaredLibrarySpecification(
             topLevelFlags, legacyRewritingFlags, libraryCompilation);
-    origin = null;
+    this.origin = null;
     return config;
   }
 
@@ -127,8 +154,7 @@
   }
 
   private HumanRewritingFlags parseRewritingFlags() {
-    HumanRewritingFlags.Builder builder =
-        HumanRewritingFlags.builder(dexItemFactory, reporter, origin);
+    HumanRewritingFlags.Builder builder = HumanRewritingFlags.builder(reporter, origin);
     JsonElement commonFlags = required(jsonConfig, COMMON_FLAGS_KEY);
     JsonElement libraryFlags = required(jsonConfig, LIBRARY_FLAGS_KEY);
     JsonElement programFlags = required(jsonConfig, PROGRAM_FLAGS_KEY);
@@ -140,11 +166,22 @@
   }
 
   HumanTopLevelFlags parseTopLevelFlags(
-      String jsonConfigString, Consumer<HumanTopLevelFlags.Builder> topLevelFlagAmender) {
+      String jsonConfigString, Consumer<TopLevelFlagsBuilder<?>> topLevelFlagAmender) {
     HumanTopLevelFlags.Builder builder = HumanTopLevelFlags.builder();
 
     builder.setJsonSource(jsonConfigString);
 
+    JsonElement formatVersionElement = required(jsonConfig, CONFIGURATION_FORMAT_VERSION_KEY);
+    int formatVersion = formatVersionElement.getAsInt();
+    if (formatVersion != CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION) {
+      reporter.warning(
+          new StringDiagnostic(
+              "Human desugared library specification version mismatches the parser "
+                  + "expected version. This is allowed and should happen only while extending "
+                  + "the specifications.",
+              origin));
+    }
+
     String identifier = required(jsonConfig, IDENTIFIER_KEY).getAsString();
     builder.setDesugaredLibraryIdentifier(identifier);
     builder.setSynthesizedLibraryClassesPackagePrefix(
@@ -191,46 +228,80 @@
         builder.putRewritePrefix(rewritePrefix.getKey(), rewritePrefix.getValue().getAsString());
       }
     }
-    if (jsonFlagSet.has(RETARGET_LIB_MEMBER_KEY)) {
+    if (jsonFlagSet.has(REWRITE_DERIVED_PREFIX_KEY)) {
+      for (Map.Entry<String, JsonElement> prefixToMatch :
+          jsonFlagSet.get(REWRITE_DERIVED_PREFIX_KEY).getAsJsonObject().entrySet()) {
+        for (Entry<String, JsonElement> rewriteRule :
+            prefixToMatch.getValue().getAsJsonObject().entrySet()) {
+          builder.putRewriteDerivedPrefix(
+              prefixToMatch.getKey(), rewriteRule.getKey(), rewriteRule.getValue().getAsString());
+        }
+      }
+    }
+    if (jsonFlagSet.has(RETARGET_METHOD_KEY)) {
       for (Map.Entry<String, JsonElement> retarget :
-          jsonFlagSet.get(RETARGET_LIB_MEMBER_KEY).getAsJsonObject().entrySet()) {
-        builder.putRetargetCoreLibMember(retarget.getKey(), retarget.getValue().getAsString());
+          jsonFlagSet.get(RETARGET_METHOD_KEY).getAsJsonObject().entrySet()) {
+        builder.retargetMethod(
+            parseMethod(retarget.getKey()),
+            stringDescriptorToDexType(retarget.getValue().getAsString()));
       }
     }
     if (jsonFlagSet.has(BACKPORT_KEY)) {
       for (Map.Entry<String, JsonElement> backport :
           jsonFlagSet.get(BACKPORT_KEY).getAsJsonObject().entrySet()) {
-        builder.putBackportCoreLibraryMember(backport.getKey(), backport.getValue().getAsString());
+        builder.putLegacyBackport(
+            stringDescriptorToDexType(backport.getKey()),
+            stringDescriptorToDexType(backport.getValue().getAsString()));
       }
     }
     if (jsonFlagSet.has(EMULATE_INTERFACE_KEY)) {
       for (Map.Entry<String, JsonElement> itf :
           jsonFlagSet.get(EMULATE_INTERFACE_KEY).getAsJsonObject().entrySet()) {
-        builder.putEmulateLibraryInterface(itf.getKey(), itf.getValue().getAsString());
+        builder.putEmulatedInterface(
+            stringDescriptorToDexType(itf.getKey()),
+            stringDescriptorToDexType(itf.getValue().getAsString()));
       }
     }
     if (jsonFlagSet.has(CUSTOM_CONVERSION_KEY)) {
       for (Map.Entry<String, JsonElement> conversion :
           jsonFlagSet.get(CUSTOM_CONVERSION_KEY).getAsJsonObject().entrySet()) {
-        builder.putCustomConversion(conversion.getKey(), conversion.getValue().getAsString());
+        builder.putCustomConversion(
+            stringDescriptorToDexType(conversion.getKey()),
+            stringDescriptorToDexType(conversion.getValue().getAsString()));
       }
     }
     if (jsonFlagSet.has(WRAPPER_CONVERSION_KEY)) {
       for (JsonElement wrapper : jsonFlagSet.get(WRAPPER_CONVERSION_KEY).getAsJsonArray()) {
-        builder.addWrapperConversion(wrapper.getAsString());
+        builder.addWrapperConversion(stringDescriptorToDexType(wrapper.getAsString()));
       }
     }
     if (jsonFlagSet.has(DONT_REWRITE_KEY)) {
       JsonArray dontRewrite = jsonFlagSet.get(DONT_REWRITE_KEY).getAsJsonArray();
       for (JsonElement rewrite : dontRewrite) {
-        builder.addDontRewriteInvocation(rewrite.getAsString());
+        builder.addDontRewriteInvocation(parseMethod(rewrite.getAsString()));
       }
     }
-    if (jsonFlagSet.has(DONT_RETARGET_LIB_MEMBER_KEY)) {
-      JsonArray dontRetarget = jsonFlagSet.get(DONT_RETARGET_LIB_MEMBER_KEY).getAsJsonArray();
+    if (jsonFlagSet.has(DONT_RETARGET_KEY)) {
+      JsonArray dontRetarget = jsonFlagSet.get(DONT_RETARGET_KEY).getAsJsonArray();
       for (JsonElement rewrite : dontRetarget) {
-        builder.addDontRetargetLibMember(rewrite.getAsString());
+        builder.addDontRetargetLibMember(stringDescriptorToDexType(rewrite.getAsString()));
       }
     }
+    if (jsonFlagSet.has(AMEND_LIBRARY_METHOD_KEY)) {
+      JsonArray amendLibraryMember = jsonFlagSet.get(AMEND_LIBRARY_METHOD_KEY).getAsJsonArray();
+      for (JsonElement amend : amendLibraryMember) {
+        methodParser.parseMethod(amend.getAsString());
+        builder.amendLibraryMethod(methodParser.getMethod(), methodParser.getFlags());
+      }
+    }
+  }
+
+  private DexMethod parseMethod(String signature) {
+    methodParser.parseMethod(signature);
+    return methodParser.getMethod();
+  }
+
+  private DexType stringDescriptorToDexType(String stringClass) {
+    return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(stringClass));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java
new file mode 100644
index 0000000..b4bac60
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanMethodParser.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HumanMethodParser extends AbstractMethodParser {
+
+  // Values accumulated while parsing.
+  private MethodAccessFlags.Builder flagBuilder;
+  private DexType returnType;
+  private DexType holder;
+  private DexString methodName;
+  private List<DexType> argTypes;
+  // Resulting values.
+  private DexMethod method;
+  private MethodAccessFlags flags;
+
+  protected HumanMethodParser(DexItemFactory factory) {
+    super(factory);
+  }
+
+  private boolean parsingFinished() {
+    return method != null;
+  }
+
+  public DexMethod getMethod() {
+    assert parsingFinished();
+    return method;
+  }
+
+  public MethodAccessFlags getFlags() {
+    assert parsingFinished();
+    return flags;
+  }
+
+  @Override
+  protected void modifier(int access) {
+    assert !parsingFinished();
+    flagBuilder.set(access);
+  }
+
+  @Override
+  protected void holderType(DexType type) {
+    assert !parsingFinished();
+    holder = type;
+  }
+
+  @Override
+  protected void methodName(DexString name) {
+    assert !parsingFinished();
+    methodName = name;
+  }
+
+  @Override
+  protected void methodStart() {
+    flagBuilder = MethodAccessFlags.builder();
+    returnType = null;
+    holder = null;
+    methodName = null;
+    argTypes = new ArrayList<>();
+    method = null;
+    flags = null;
+  }
+
+  @Override
+  protected void methodEnd() {
+    DexProto proto = factory.createProto(returnType, argTypes);
+    method = factory.createMethod(holder, proto, methodName);
+    flags = flagBuilder.build();
+  }
+
+  @Override
+  protected void returnType(DexType type) {
+    assert !parsingFinished();
+    returnType = type;
+  }
+
+  @Override
+  protected void argType(DexType type) {
+    assert !parsingFinished();
+    argTypes.add(type);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index bf87cd8..ece6fe0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -4,14 +4,10 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableMap;
@@ -27,31 +23,37 @@
 public class HumanRewritingFlags {
 
   private final Map<String, String> rewritePrefix;
-  private final Map<DexType, DexType> emulateLibraryInterface;
-  private final Map<DexMethod, DexType> retargetCoreLibMember;
-  private final Map<DexType, DexType> backportCoreLibraryMember;
+  private final Map<String, Map<String, String>> rewriteDerivedPrefix;
+  private final Map<DexType, DexType> emulatedInterfaces;
+  private final Map<DexMethod, DexType> retargetMethod;
+  private final Map<DexType, DexType> legacyBackport;
   private final Map<DexType, DexType> customConversions;
   private final Set<DexMethod> dontRewriteInvocation;
-  private final Set<DexType> dontRetargetLibMember;
+  private final Set<DexType> dontRetarget;
   private final Set<DexType> wrapperConversions;
+  private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
 
   HumanRewritingFlags(
       Map<String, String> rewritePrefix,
+      Map<String, Map<String, String>> rewriteDerivedPrefix,
       Map<DexType, DexType> emulateLibraryInterface,
-      Map<DexMethod, DexType> retargetCoreLibMember,
-      Map<DexType, DexType> backportCoreLibraryMember,
-      Map<DexType, DexType> customConversions,
+      Map<DexMethod, DexType> retargetMethod,
+      Map<DexType, DexType> legacyBackport,
+      Map<DexType, DexType> customConversion,
       Set<DexMethod> dontRewriteInvocation,
-      Set<DexType> dontRetargetLibMember,
-      Set<DexType> wrapperConversions) {
+      Set<DexType> dontRetarget,
+      Set<DexType> wrapperConversion,
+      Map<DexMethod, MethodAccessFlags> amendLibraryMethod) {
     this.rewritePrefix = rewritePrefix;
-    this.emulateLibraryInterface = emulateLibraryInterface;
-    this.retargetCoreLibMember = retargetCoreLibMember;
-    this.backportCoreLibraryMember = backportCoreLibraryMember;
-    this.customConversions = customConversions;
+    this.rewriteDerivedPrefix = rewriteDerivedPrefix;
+    this.emulatedInterfaces = emulateLibraryInterface;
+    this.retargetMethod = retargetMethod;
+    this.legacyBackport = legacyBackport;
+    this.customConversions = customConversion;
     this.dontRewriteInvocation = dontRewriteInvocation;
-    this.dontRetargetLibMember = dontRetargetLibMember;
-    this.wrapperConversions = wrapperConversions;
+    this.dontRetarget = dontRetarget;
+    this.wrapperConversions = wrapperConversion;
+    this.amendLibraryMethod = amendLibraryMethod;
   }
 
   public static HumanRewritingFlags empty() {
@@ -61,51 +63,51 @@
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
+        ImmutableMap.of(),
         ImmutableSet.of(),
         ImmutableSet.of(),
-        ImmutableSet.of());
+        ImmutableSet.of(),
+        ImmutableMap.of());
   }
 
-  public static HumanRewritingFlags withOnlyRewritePrefixForTesting(
-      Map<String, String> prefix, InternalOptions options) {
-    Builder builder = builder(options.dexItemFactory(), options.reporter, Origin.unknown());
-    prefix.forEach(builder::putRewritePrefix);
-    return builder.build();
+  public static Builder builder(Reporter reporter, Origin origin) {
+    return new Builder(reporter, origin);
   }
 
-  public static Builder builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
-    return new Builder(dexItemFactory, reporter, origin);
-  }
-
-  public Builder newBuilder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
+  public Builder newBuilder(Reporter reporter, Origin origin) {
     return new Builder(
-        dexItemFactory,
         reporter,
         origin,
         rewritePrefix,
-        emulateLibraryInterface,
-        retargetCoreLibMember,
-        backportCoreLibraryMember,
+        rewriteDerivedPrefix,
+        emulatedInterfaces,
+        retargetMethod,
+        legacyBackport,
         customConversions,
         dontRewriteInvocation,
-        dontRetargetLibMember,
-        wrapperConversions);
+        dontRetarget,
+        wrapperConversions,
+        amendLibraryMethod);
   }
 
   public Map<String, String> getRewritePrefix() {
     return rewritePrefix;
   }
 
-  public Map<DexType, DexType> getEmulateLibraryInterface() {
-    return emulateLibraryInterface;
+  public Map<String, Map<String, String>> getRewriteDerivedPrefix() {
+    return rewriteDerivedPrefix;
   }
 
-  public Map<DexMethod, DexType> getRetargetCoreLibMember() {
-    return retargetCoreLibMember;
+  public Map<DexType, DexType> getEmulatedInterfaces() {
+    return emulatedInterfaces;
   }
 
-  public Map<DexType, DexType> getBackportCoreLibraryMember() {
-    return backportCoreLibraryMember;
+  public Map<DexMethod, DexType> getRetargetMethod() {
+    return retargetMethod;
+  }
+
+  public Map<DexType, DexType> getLegacyBackport() {
+    return legacyBackport;
   }
 
   public Map<DexType, DexType> getCustomConversions() {
@@ -116,72 +118,85 @@
     return dontRewriteInvocation;
   }
 
-  public Set<DexType> getDontRetargetLibMember() {
-    return dontRetargetLibMember;
+  public Set<DexType> getDontRetarget() {
+    return dontRetarget;
   }
 
   public Set<DexType> getWrapperConversions() {
     return wrapperConversions;
   }
 
+  public Map<DexMethod, MethodAccessFlags> getAmendLibraryMethod() {
+    return amendLibraryMethod;
+  }
+
+  public boolean isEmpty() {
+    return rewritePrefix.isEmpty()
+        && rewriteDerivedPrefix.isEmpty()
+        && emulatedInterfaces.isEmpty()
+        && retargetMethod.isEmpty();
+  }
+
   public static class Builder {
 
-    private static final String SEPARATORS = "\\s+|,\\s+|#|\\(|\\)";
-
-    private final DexItemFactory factory;
     private final Reporter reporter;
     private final Origin origin;
 
     private final Map<String, String> rewritePrefix;
-    private final Map<DexType, DexType> emulateLibraryInterface;
-    private final Map<DexMethod, DexType> retargetCoreLibMember;
-    private final Map<DexType, DexType> backportCoreLibraryMember;
+    private final Map<String, Map<String, String>> rewriteDerivedPrefix;
+    private final Map<DexType, DexType> emulatedInterfaces;
+    private final Map<DexMethod, DexType> retargetMethod;
+    private final Map<DexType, DexType> legacyBackport;
     private final Map<DexType, DexType> customConversions;
     private final Set<DexMethod> dontRewriteInvocation;
-    private final Set<DexType> dontRetargetLibMember;
+    private final Set<DexType> dontRetarget;
     private final Set<DexType> wrapperConversions;
+    private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
 
-    Builder(DexItemFactory factory, Reporter reporter, Origin origin) {
+    Builder(Reporter reporter, Origin origin) {
       this(
-          factory,
           reporter,
           origin,
           new HashMap<>(),
+          new HashMap<>(),
           new IdentityHashMap<>(),
           new IdentityHashMap<>(),
           new IdentityHashMap<>(),
           new IdentityHashMap<>(),
           Sets.newIdentityHashSet(),
           Sets.newIdentityHashSet(),
-          Sets.newIdentityHashSet());
+          Sets.newIdentityHashSet(),
+          new IdentityHashMap<>());
     }
 
     Builder(
-        DexItemFactory factory,
         Reporter reporter,
         Origin origin,
         Map<String, String> rewritePrefix,
+        Map<String, Map<String, String>> rewriteDerivedPrefix,
         Map<DexType, DexType> emulateLibraryInterface,
         Map<DexMethod, DexType> retargetCoreLibMember,
         Map<DexType, DexType> backportCoreLibraryMember,
         Map<DexType, DexType> customConversions,
         Set<DexMethod> dontRewriteInvocation,
         Set<DexType> dontRetargetLibMember,
-        Set<DexType> wrapperConversions) {
-      this.factory = factory;
+        Set<DexType> wrapperConversions,
+        Map<DexMethod, MethodAccessFlags> amendLibrary) {
       this.reporter = reporter;
       this.origin = origin;
       this.rewritePrefix = new HashMap<>(rewritePrefix);
-      this.emulateLibraryInterface = new IdentityHashMap<>(emulateLibraryInterface);
-      this.retargetCoreLibMember = new IdentityHashMap<>(retargetCoreLibMember);
-      this.backportCoreLibraryMember = new IdentityHashMap<>(backportCoreLibraryMember);
+      this.rewriteDerivedPrefix = new HashMap<>(rewriteDerivedPrefix);
+      this.emulatedInterfaces = new IdentityHashMap<>(emulateLibraryInterface);
+      this.retargetMethod = new IdentityHashMap<>(retargetCoreLibMember);
+      this.legacyBackport = new IdentityHashMap<>(backportCoreLibraryMember);
       this.customConversions = new IdentityHashMap<>(customConversions);
       this.dontRewriteInvocation = Sets.newIdentityHashSet();
       this.dontRewriteInvocation.addAll(dontRewriteInvocation);
-      this.dontRetargetLibMember = Sets.newIdentityHashSet();
-      this.dontRetargetLibMember.addAll(dontRetargetLibMember);
+      this.dontRetarget = Sets.newIdentityHashSet();
+      this.dontRetarget.addAll(dontRetargetLibMember);
       this.wrapperConversions = Sets.newIdentityHashSet();
       this.wrapperConversions.addAll(wrapperConversions);
+      this.amendLibraryMethod = new IdentityHashMap<>(amendLibrary);
     }
 
     // Utility to set values.
@@ -209,30 +224,27 @@
       return this;
     }
 
-    public Builder putEmulateLibraryInterface(
-        String emulateLibraryItf, String rewrittenEmulateLibraryItf) {
-      DexType interfaceType = stringClassToDexType(emulateLibraryItf);
-      DexType rewrittenType = stringClassToDexType(rewrittenEmulateLibraryItf);
-      putEmulateLibraryInterface(interfaceType, rewrittenType);
+    public Builder putRewriteDerivedPrefix(
+        String prefixToMatch, String prefixToRewrite, String rewrittenPrefix) {
+      Map<String, String> map =
+          rewriteDerivedPrefix.computeIfAbsent(prefixToMatch, k -> new HashMap<>());
+      put(
+          map,
+          prefixToRewrite,
+          rewrittenPrefix,
+          HumanDesugaredLibrarySpecificationParser.REWRITE_DERIVED_PREFIX_KEY);
       return this;
     }
 
-    public Builder putEmulateLibraryInterface(DexType interfaceType, DexType rewrittenType) {
+    public Builder putEmulatedInterface(DexType interfaceType, DexType rewrittenType) {
       put(
-          emulateLibraryInterface,
+          emulatedInterfaces,
           interfaceType,
           rewrittenType,
           HumanDesugaredLibrarySpecificationParser.EMULATE_INTERFACE_KEY);
       return this;
     }
 
-    public Builder putCustomConversion(String type, String conversionHolder) {
-      DexType dexType = stringClassToDexType(type);
-      DexType conversionType = stringClassToDexType(conversionHolder);
-      putCustomConversion(dexType, conversionType);
-      return this;
-    }
-
     public Builder putCustomConversion(DexType dexType, DexType conversionType) {
       put(
           customConversions,
@@ -242,100 +254,57 @@
       return this;
     }
 
-    public Builder addWrapperConversion(String type) {
-      DexType dexType = stringClassToDexType(type);
-      addWrapperConversion(dexType);
-      return this;
-    }
-
     public Builder addWrapperConversion(DexType dexType) {
       wrapperConversions.add(dexType);
       return this;
     }
 
-    public Builder putRetargetCoreLibMember(String retarget, String rewrittenRetarget) {
-      DexMethod key = parseMethod(retarget);
-      DexType rewrittenType = stringClassToDexType(rewrittenRetarget);
-      putRetargetCoreLibMember(key, rewrittenType);
-      return this;
-    }
-
-    public Builder putRetargetCoreLibMember(DexMethod key, DexType rewrittenType) {
+    public Builder retargetMethod(DexMethod key, DexType rewrittenType) {
       put(
-          retargetCoreLibMember,
+          retargetMethod,
           key,
           rewrittenType,
-          HumanDesugaredLibrarySpecificationParser.RETARGET_LIB_MEMBER_KEY);
+          HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY);
       return this;
     }
 
-    public Builder putBackportCoreLibraryMember(String backport, String rewrittenBackport) {
-      DexType backportType = stringClassToDexType(backport);
-      DexType rewrittenBackportType = stringClassToDexType(rewrittenBackport);
-      putBackportCoreLibraryMember(backportType, rewrittenBackportType);
-      return this;
-    }
-
-    public Builder putBackportCoreLibraryMember(
-        DexType backportType, DexType rewrittenBackportType) {
+    public Builder putLegacyBackport(DexType backportType, DexType rewrittenBackportType) {
       put(
-          backportCoreLibraryMember,
+          legacyBackport,
           backportType,
           rewrittenBackportType,
           HumanDesugaredLibrarySpecificationParser.BACKPORT_KEY);
       return this;
     }
 
-    public Builder addDontRewriteInvocation(String dontRewriteInvocation) {
-      DexMethod dontRewrite = parseMethod(dontRewriteInvocation);
-      addDontRewriteInvocation(dontRewrite);
-      return this;
-    }
-
     public Builder addDontRewriteInvocation(DexMethod dontRewrite) {
-      this.dontRewriteInvocation.add(dontRewrite);
-      return this;
-    }
-
-    public Builder addDontRetargetLibMember(String dontRetargetLibMember) {
-      addDontRetargetLibMember(stringClassToDexType(dontRetargetLibMember));
+      dontRewriteInvocation.add(dontRewrite);
       return this;
     }
 
     public Builder addDontRetargetLibMember(DexType dontRetargetLibMember) {
-      this.dontRetargetLibMember.add(dontRetargetLibMember);
+      dontRetarget.add(dontRetargetLibMember);
       return this;
     }
 
-    private DexMethod parseMethod(String signature) {
-      String[] split = signature.split(SEPARATORS);
-      assert split.length >= 3;
-      DexType returnType = factory.createType(DescriptorUtils.javaTypeToDescriptor(split[0]));
-      DexType holderType = factory.createType(DescriptorUtils.javaTypeToDescriptor(split[1]));
-      DexString name = factory.createString(split[2]);
-      DexType[] argTypes = new DexType[split.length - 3];
-      for (int i = 3; i < split.length; i++) {
-        argTypes[i - 3] = factory.createType(DescriptorUtils.javaTypeToDescriptor(split[i]));
-      }
-      DexProto proto = factory.createProto(returnType, argTypes);
-      return factory.createMethod(holderType, proto, name);
-    }
-
-    private DexType stringClassToDexType(String stringClass) {
-      return factory.createType(DescriptorUtils.javaTypeToDescriptor(stringClass));
+    public Builder amendLibraryMethod(DexMethod member, MethodAccessFlags flags) {
+      amendLibraryMethod.put(member, flags);
+      return this;
     }
 
     public HumanRewritingFlags build() {
       validate();
       return new HumanRewritingFlags(
           ImmutableMap.copyOf(rewritePrefix),
-          ImmutableMap.copyOf(emulateLibraryInterface),
-          ImmutableMap.copyOf(retargetCoreLibMember),
-          ImmutableMap.copyOf(backportCoreLibraryMember),
+          ImmutableMap.copyOf(rewriteDerivedPrefix),
+          ImmutableMap.copyOf(emulatedInterfaces),
+          ImmutableMap.copyOf(retargetMethod),
+          ImmutableMap.copyOf(legacyBackport),
           ImmutableMap.copyOf(customConversions),
           ImmutableSet.copyOf(dontRewriteInvocation),
-          ImmutableSet.copyOf(dontRetargetLibMember),
-          ImmutableSet.copyOf(wrapperConversions));
+          ImmutableSet.copyOf(dontRetarget),
+          ImmutableSet.copyOf(wrapperConversions),
+          ImmutableMap.copyOf(amendLibraryMethod));
     }
 
     private void validate() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java
index 80a13b5..409cf74 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanTopLevelFlags.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
+import com.android.tools.r8.ir.desugar.desugaredlibrary.TopLevelFlagsBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
@@ -77,7 +78,7 @@
     return extraKeepRules;
   }
 
-  public static class Builder {
+  public static class Builder implements TopLevelFlagsBuilder<Builder> {
 
     private AndroidApiLevel requiredCompilationAPILevel;
     private String synthesizedLibraryClassesPackagePrefix;
@@ -108,6 +109,7 @@
       return this;
     }
 
+    @Override
     public Builder setSupportAllCallbacksFromLibrary(boolean supportAllCallbacksFromLibrary) {
       this.supportAllCallbacksFromLibrary = supportAllCallbacksFromLibrary;
       return this;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
index c79fad3..afc7dd6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
@@ -5,8 +5,9 @@
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
 import com.android.tools.r8.graph.DexItem;
-import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.Reporter;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@@ -20,7 +21,6 @@
 
   public static void deduplicateFlags(
       MultiAPILevelHumanDesugaredLibrarySpecification specification,
-      DexItemFactory factory,
       Reporter reporter) {
 
     IntArraySet apis = new IntArraySet();
@@ -29,13 +29,12 @@
     apis.addAll(specification.getProgramFlags().keySet());
 
     for (Integer api : apis) {
-      deduplicateFlags(specification, factory, reporter, api);
+      deduplicateFlags(specification, reporter, api);
     }
   }
 
   private static void deduplicateFlags(
       MultiAPILevelHumanDesugaredLibrarySpecification specification,
-      DexItemFactory factory,
       Reporter reporter,
       int api) {
 
@@ -53,21 +52,29 @@
     Origin origin = specification.getOrigin();
     HumanRewritingFlags.Builder commonBuilder =
         commonFlags.get(api) == null
-            ? HumanRewritingFlags.builder(factory, reporter, origin)
-            : commonFlags.get(api).newBuilder(factory, reporter, origin);
-    HumanRewritingFlags.Builder libraryBuilder =
-        HumanRewritingFlags.builder(factory, reporter, origin);
-    HumanRewritingFlags.Builder programBuilder =
-        HumanRewritingFlags.builder(factory, reporter, origin);
+            ? HumanRewritingFlags.builder(reporter, origin)
+            : commonFlags.get(api).newBuilder(reporter, origin);
+    HumanRewritingFlags.Builder libraryBuilder = HumanRewritingFlags.builder(reporter, origin);
+    HumanRewritingFlags.Builder programBuilder = HumanRewritingFlags.builder(reporter, origin);
 
     // Iterate over all library/program flags, add them in common if also in the other, else add
     // them to library/program.
     deduplicateFlags(library, program, commonBuilder, libraryBuilder);
     deduplicateFlags(program, library, commonBuilder, programBuilder);
 
-    commonFlags.put(api, commonBuilder.build());
-    libraryFlags.put(api, libraryBuilder.build());
-    programFlags.put(api, programBuilder.build());
+    putNewFlags(api, commonFlags, commonBuilder);
+    putNewFlags(api, libraryFlags, libraryBuilder);
+    putNewFlags(api, programFlags, programBuilder);
+  }
+
+  private static void putNewFlags(
+      int api, Int2ObjectMap<HumanRewritingFlags> flags, HumanRewritingFlags.Builder builder) {
+    HumanRewritingFlags build = builder.build();
+    if (build.isEmpty()) {
+      flags.remove(api);
+    } else {
+      flags.put(api, build);
+    }
   }
 
   private static void deduplicateFlags(
@@ -76,22 +83,23 @@
       HumanRewritingFlags.Builder commonBuilder,
       HumanRewritingFlags.Builder builder) {
     deduplicateRewritePrefix(flags, otherFlags, commonBuilder, builder);
+    deduplicateRewriteDifferentPrefix(flags, otherFlags, commonBuilder, builder);
 
     deduplicateFlags(
-        flags.getEmulateLibraryInterface(),
-        otherFlags.getEmulateLibraryInterface(),
-        commonBuilder::putEmulateLibraryInterface,
-        builder::putEmulateLibraryInterface);
+        flags.getEmulatedInterfaces(),
+        otherFlags.getEmulatedInterfaces(),
+        commonBuilder::putEmulatedInterface,
+        builder::putEmulatedInterface);
     deduplicateFlags(
-        flags.getRetargetCoreLibMember(),
-        otherFlags.getRetargetCoreLibMember(),
-        commonBuilder::putRetargetCoreLibMember,
-        builder::putRetargetCoreLibMember);
+        flags.getRetargetMethod(),
+        otherFlags.getRetargetMethod(),
+        commonBuilder::retargetMethod,
+        builder::retargetMethod);
     deduplicateFlags(
-        flags.getBackportCoreLibraryMember(),
-        otherFlags.getBackportCoreLibraryMember(),
-        commonBuilder::putBackportCoreLibraryMember,
-        builder::putBackportCoreLibraryMember);
+        flags.getLegacyBackport(),
+        otherFlags.getLegacyBackport(),
+        commonBuilder::putLegacyBackport,
+        builder::putLegacyBackport);
     deduplicateFlags(
         flags.getCustomConversions(),
         otherFlags.getCustomConversions(),
@@ -104,8 +112,8 @@
         commonBuilder::addDontRewriteInvocation,
         builder::addDontRewriteInvocation);
     deduplicateFlags(
-        flags.getDontRetargetLibMember(),
-        otherFlags.getDontRetargetLibMember(),
+        flags.getDontRetarget(),
+        otherFlags.getDontRetarget(),
         commonBuilder::addDontRetargetLibMember,
         builder::addDontRetargetLibMember);
     deduplicateFlags(
@@ -113,6 +121,53 @@
         otherFlags.getWrapperConversions(),
         commonBuilder::addWrapperConversion,
         builder::addWrapperConversion);
+
+    deduplicateAmendLibraryMemberFlags(flags, otherFlags, commonBuilder, builder);
+  }
+
+  private static void deduplicateAmendLibraryMemberFlags(
+      HumanRewritingFlags flags,
+      HumanRewritingFlags otherFlags,
+      HumanRewritingFlags.Builder commonBuilder,
+      HumanRewritingFlags.Builder builder) {
+    Map<DexMethod, MethodAccessFlags> other = otherFlags.getAmendLibraryMethod();
+    flags
+        .getAmendLibraryMethod()
+        .forEach(
+            (k, v) -> {
+              if (other.get(k) == v) {
+                commonBuilder.amendLibraryMethod(k, v);
+              } else {
+                builder.amendLibraryMethod(k, v);
+              }
+            });
+  }
+
+  private static void deduplicateRewriteDifferentPrefix(
+      HumanRewritingFlags flags,
+      HumanRewritingFlags otherFlags,
+      HumanRewritingFlags.Builder commonBuilder,
+      HumanRewritingFlags.Builder builder) {
+    flags
+        .getRewriteDerivedPrefix()
+        .forEach(
+            (prefixToMatch, rewriteRules) -> {
+              if (!otherFlags.getRewriteDerivedPrefix().containsKey(prefixToMatch)) {
+                rewriteRules.forEach(
+                    (k, v) -> builder.putRewriteDerivedPrefix(prefixToMatch, k, v));
+              } else {
+                Map<String, String> otherMap =
+                    otherFlags.getRewriteDerivedPrefix().get(prefixToMatch);
+                rewriteRules.forEach(
+                    (k, v) -> {
+                      if (otherMap.containsKey(k) && otherMap.get(k).equals(v)) {
+                        commonBuilder.putRewriteDerivedPrefix(prefixToMatch, k, v);
+                      } else {
+                        builder.putRewriteDerivedPrefix(prefixToMatch, k, v);
+                      }
+                    });
+              }
+            });
   }
 
   private static void deduplicateRewritePrefix(
@@ -124,7 +179,7 @@
         .getRewritePrefix()
         .forEach(
             (k, v) -> {
-              if (otherFlags.getRewritePrefix().get(k) != null
+              if (otherFlags.getRewritePrefix().containsKey(k)
                   && otherFlags.getRewritePrefix().get(k).equals(v)) {
                 commonBuilder.putRewritePrefix(k, v);
               } else {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
index fd76244..97749d6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -4,8 +4,27 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
-import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.*;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.AMEND_LIBRARY_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_BELOW_OR_EQUAL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.BACKPORT_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.COMMON_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.CUSTOM_CONVERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_RETARGET_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_REWRITE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.EMULATE_INTERFACE_KEY;
 import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.IDENTIFIER_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.LIBRARY_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.PROGRAM_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REQUIRED_COMPILATION_API_LEVEL_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_DERIVED_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SHRINKER_CONFIG_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.WRAPPER_CONVERSION_KEY;
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.StringConsumer;
@@ -13,10 +32,12 @@
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
 import com.google.common.collect.Sets;
 import com.google.gson.Gson;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -36,12 +57,13 @@
       MultiAPILevelHumanDesugaredLibrarySpecification humanSpec, StringConsumer output) {
     HashMap<String, Object> toJson = new LinkedHashMap<>();
     toJson.put(IDENTIFIER_KEY, humanSpec.getTopLevelFlags().getIdentifier());
+    toJson.put(CONFIGURATION_FORMAT_VERSION_KEY, CURRENT_HUMAN_CONFIGURATION_FORMAT_VERSION);
     toJson.put(
         REQUIRED_COMPILATION_API_LEVEL_KEY,
         humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel().getLevel());
     toJson.put(
         SYNTHESIZED_LIBRARY_CLASSES_PACKAGE_PREFIX_KEY,
-        humanSpec.getTopLevelFlags().getSynthesizedLibraryClassesPackagePrefix());
+        humanSpec.getTopLevelFlags().getSynthesizedLibraryClassesPackagePrefix().replace('/', '.'));
     toJson.put(
         SUPPORT_ALL_CALLBACKS_FROM_LIBRARY_KEY,
         humanSpec.getTopLevelFlags().supportAllCallbacksFromLibrary());
@@ -60,39 +82,58 @@
   private List<Object> rewritingFlagsToString(
       Int2ObjectMap<HumanRewritingFlags> rewritingFlagsMap) {
     ArrayList<Object> list = new ArrayList<>();
-    rewritingFlagsMap.forEach(
-        (apiBelowOrEqual, flags) -> {
-          HashMap<String, Object> toJson = new LinkedHashMap<>();
-          toJson.put(API_LEVEL_BELOW_OR_EQUAL_KEY, apiBelowOrEqual);
-          if (!flags.getRewritePrefix().isEmpty()) {
-            toJson.put(REWRITE_PREFIX_KEY, new TreeMap<>(flags.getRewritePrefix()));
-          }
-          if (!flags.getEmulateLibraryInterface().isEmpty()) {
-            toJson.put(EMULATE_INTERFACE_KEY, mapToString(flags.getEmulateLibraryInterface()));
-          }
-          if (!flags.getDontRewriteInvocation().isEmpty()) {
-            toJson.put(DONT_REWRITE_KEY, setToString(flags.getDontRewriteInvocation()));
-          }
-          if (!flags.getRetargetCoreLibMember().isEmpty()) {
-            toJson.put(RETARGET_LIB_MEMBER_KEY, mapToString(flags.getRetargetCoreLibMember()));
-          }
-          if (!flags.getDontRetargetLibMember().isEmpty()) {
-            toJson.put(DONT_RETARGET_LIB_MEMBER_KEY, setToString(flags.getDontRetargetLibMember()));
-          }
-          if (!flags.getBackportCoreLibraryMember().isEmpty()) {
-            toJson.put(BACKPORT_KEY, mapToString(flags.getBackportCoreLibraryMember()));
-          }
-          if (!flags.getWrapperConversions().isEmpty()) {
-            toJson.put(WRAPPER_CONVERSION_KEY, setToString(flags.getWrapperConversions()));
-          }
-          if (!flags.getCustomConversions().isEmpty()) {
-            toJson.put(CUSTOM_CONVERSION_KEY, mapToString(flags.getCustomConversions()));
-          }
-          list.add(toJson);
-        });
+    ArrayList<Integer> apis = new ArrayList<>(rewritingFlagsMap.keySet());
+    apis.sort(Comparator.reverseOrder());
+    for (int apiBelowOrEqual : apis) {
+      HumanRewritingFlags flags = rewritingFlagsMap.get(apiBelowOrEqual);
+      HashMap<String, Object> toJson = new LinkedHashMap<>();
+      toJson.put(API_LEVEL_BELOW_OR_EQUAL_KEY, apiBelowOrEqual);
+      if (!flags.getRewritePrefix().isEmpty()) {
+        toJson.put(REWRITE_PREFIX_KEY, new TreeMap<>(flags.getRewritePrefix()));
+      }
+      if (!flags.getRewriteDerivedPrefix().isEmpty()) {
+        TreeMap<String, Map<String, String>> rewriteDerivedPrefix = new TreeMap<>();
+        flags
+            .getRewriteDerivedPrefix()
+            .forEach((k, v) -> rewriteDerivedPrefix.put(k, new TreeMap<>(v)));
+        toJson.put(REWRITE_DERIVED_PREFIX_KEY, rewriteDerivedPrefix);
+      }
+      if (!flags.getEmulatedInterfaces().isEmpty()) {
+        toJson.put(EMULATE_INTERFACE_KEY, mapToString(flags.getEmulatedInterfaces()));
+      }
+      if (!flags.getDontRewriteInvocation().isEmpty()) {
+        toJson.put(DONT_REWRITE_KEY, setToString(flags.getDontRewriteInvocation()));
+      }
+      if (!flags.getRetargetMethod().isEmpty()) {
+        toJson.put(RETARGET_METHOD_KEY, mapToString(flags.getRetargetMethod()));
+      }
+      if (!flags.getDontRetarget().isEmpty()) {
+        toJson.put(DONT_RETARGET_KEY, setToString(flags.getDontRetarget()));
+      }
+      if (!flags.getLegacyBackport().isEmpty()) {
+        toJson.put(BACKPORT_KEY, mapToString(flags.getLegacyBackport()));
+      }
+      if (!flags.getWrapperConversions().isEmpty()) {
+        toJson.put(WRAPPER_CONVERSION_KEY, setToString(flags.getWrapperConversions()));
+      }
+      if (!flags.getCustomConversions().isEmpty()) {
+        toJson.put(CUSTOM_CONVERSION_KEY, mapToString(flags.getCustomConversions()));
+      }
+      if (!flags.getAmendLibraryMethod().isEmpty()) {
+        toJson.put(AMEND_LIBRARY_METHOD_KEY, amendLibraryToString(flags.getAmendLibraryMethod()));
+      }
+      list.add(toJson);
+    }
     return list;
   }
 
+  private Set<String> amendLibraryToString(Map<DexMethod, MethodAccessFlags> amendLibraryMembers) {
+    Set<String> stringSet = Sets.newHashSet();
+    amendLibraryMembers.forEach(
+        (member, flags) -> stringSet.add(flags.toString() + " " + toString(member)));
+    return stringSet;
+  }
+
   private Set<String> setToString(Set<? extends DexItem> set) {
     Set<String> stringSet = Sets.newHashSet();
     set.forEach(e -> stringSet.add(toString(e)));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java
index 140609d..9f9fa81 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationParser.java
@@ -43,10 +43,8 @@
       int api_level_below_or_equal = required(flag, API_LEVEL_BELOW_OR_EQUAL_KEY).getAsInt();
       HumanRewritingFlags.Builder builder =
           flags.containsKey(api_level_below_or_equal)
-              ? flags
-                  .get(api_level_below_or_equal)
-                  .newBuilder(dexItemFactory(), reporter(), getOrigin())
-              : HumanRewritingFlags.builder(dexItemFactory(), reporter(), getOrigin());
+              ? flags.get(api_level_below_or_equal).newBuilder(reporter(), getOrigin())
+              : HumanRewritingFlags.builder(reporter(), getOrigin());
       parseFlags(flag, builder);
       flags.put(api_level_below_or_equal, builder.build());
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index 9ab05b9..71227cf 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -9,37 +9,28 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
+import java.io.IOException;
+import java.nio.file.Path;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-public class LegacyDesugaredLibrarySpecification {
+public class LegacyDesugaredLibrarySpecification implements DesugaredLibrarySpecification {
 
   private final boolean libraryCompilation;
   private final LegacyTopLevelFlags topLevelFlags;
   private final LegacyRewritingFlags rewritingFlags;
 
-  public static LegacyDesugaredLibrarySpecification withOnlyRewritePrefixForTesting(
-      Map<String, String> prefix, InternalOptions options) {
-    return new LegacyDesugaredLibrarySpecification(
-        LegacyTopLevelFlags.empty(),
-        LegacyRewritingFlags.withOnlyRewritePrefixForTesting(prefix, options),
-        true);
-  }
-
   public static LegacyDesugaredLibrarySpecification empty() {
     return new LegacyDesugaredLibrarySpecification(
-        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false) {
-
-      @Override
-      public boolean isEmptyConfiguration() {
-        return true;
-      }
-    };
+        LegacyTopLevelFlags.empty(), LegacyRewritingFlags.empty(), false);
   }
 
   public LegacyDesugaredLibrarySpecification(
@@ -51,6 +42,21 @@
     this.rewritingFlags = rewritingFlags;
   }
 
+  @Override
+  public boolean isEmpty() {
+    return rewritingFlags.isEmpty();
+  }
+
+  @Override
+  public boolean isLegacy() {
+    return true;
+  }
+
+  @Override
+  public LegacyDesugaredLibrarySpecification asLegacyDesugaredLibrarySpecification() {
+    return this;
+  }
+
   public LegacyTopLevelFlags getTopLevelFlags() {
     return topLevelFlags;
   }
@@ -63,29 +69,21 @@
     return topLevelFlags.supportAllCallbacksFromLibrary();
   }
 
+  @Override
   public AndroidApiLevel getRequiredCompilationApiLevel() {
     return topLevelFlags.getRequiredCompilationAPILevel();
   }
 
+  @Override
   public boolean isLibraryCompilation() {
     return libraryCompilation;
   }
 
+  @Override
   public String getSynthesizedLibraryClassesPackagePrefix() {
     return topLevelFlags.getSynthesizedLibraryClassesPackagePrefix();
   }
 
-  // TODO(b/183918843): We are currently computing a new name for the class by replacing the
-  //  initial package prefix by the synthesized library class package prefix, it would be better
-  //  to make the rewriting explicit in the desugared library json file.
-  public String convertJavaNameToDesugaredLibrary(DexType type) {
-    String prefix =
-        DescriptorUtils.getJavaTypeFromBinaryName(getSynthesizedLibraryClassesPackagePrefix());
-    String interfaceType = type.toString();
-    int firstPackage = interfaceType.indexOf('.');
-    return prefix + interfaceType.substring(firstPackage + 1);
-  }
-
   public String getIdentifier() {
     return topLevelFlags.getIdentifier();
   }
@@ -146,15 +144,29 @@
     return rewritingFlags.getDontRetargetLibMember();
   }
 
+  @Override
   public List<String> getExtraKeepRules() {
     return topLevelFlags.getExtraKeepRules();
   }
 
+  @Override
   public String getJsonSource() {
     return topLevelFlags.getJsonSource();
   }
 
-  public boolean isEmptyConfiguration() {
-    return false;
+  @Override
+  public MachineDesugaredLibrarySpecification toMachineSpecification(
+      InternalOptions options, AndroidApp app) throws IOException {
+    return new LegacyToHumanSpecificationConverter()
+        .convert(this, app.getLibraryResourceProviders(), options)
+        .toMachineSpecification(options, app);
+  }
+
+  @Override
+  public MachineDesugaredLibrarySpecification toMachineSpecification(
+      InternalOptions options, Path library, Path desugaredJDKLib) throws IOException {
+    return new LegacyToHumanSpecificationConverter()
+        .convert(this, library, options)
+        .toMachineSpecification(options, library, desugaredJDKLib);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
index a3afcd9..5b6933d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecificationParser.java
@@ -4,8 +4,12 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification;
 
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isHumanSpecification;
+
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.TopLevelFlagsBuilder;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
@@ -26,7 +30,6 @@
   public static final int MAX_SUPPORTED_VERSION = 4;
   public static final SemanticVersion MIN_SUPPORTED_VERSION = new SemanticVersion(1, 0, 9);
 
-  static final String CONFIGURATION_FORMAT_VERSION_KEY = "configuration_format_version";
   static final String VERSION_KEY = "version";
   static final String GROUP_ID_KEY = "group_id";
   static final String ARTIFACT_ID_KEY = "artifact_id";
@@ -97,13 +100,26 @@
   }
 
   public LegacyDesugaredLibrarySpecification parse(StringResource stringResource) {
-    return parse(stringResource, builder -> {});
+    String jsonConfigString = parseJson(stringResource);
+    return parse(origin, jsonConfigString, jsonConfig, ignored -> {});
   }
 
   public LegacyDesugaredLibrarySpecification parse(
-      StringResource stringResource, Consumer<LegacyTopLevelFlags.Builder> topLevelFlagAmender) {
-    String jsonConfigString = parseJson(stringResource);
+      Origin origin, String jsonConfigString, JsonObject jsonConfig) {
+    return parse(origin, jsonConfigString, jsonConfig, ignored -> {});
+  }
 
+  public LegacyDesugaredLibrarySpecification parse(
+      Origin origin,
+      String jsonConfigString,
+      JsonObject jsonConfig,
+      Consumer<TopLevelFlagsBuilder<?>> topLevelFlagAmender) {
+    if (isHumanSpecification(jsonConfig, reporter, origin)) {
+      reporter.error(
+          "Attempt to parse a desugared library human specification as a legacy specification.");
+    }
+    this.origin = origin;
+    this.jsonConfig = jsonConfig;
     LegacyTopLevelFlags topLevelFlags = parseTopLevelFlags(jsonConfigString, topLevelFlagAmender);
 
     LegacyRewritingFlags legacyRewritingFlags = parseRewritingFlags();
@@ -111,7 +127,7 @@
     LegacyDesugaredLibrarySpecification config =
         new LegacyDesugaredLibrarySpecification(
             topLevelFlags, legacyRewritingFlags, libraryCompilation);
-    origin = null;
+    this.origin = null;
     return config;
   }
 
@@ -147,7 +163,7 @@
   }
 
   LegacyTopLevelFlags parseTopLevelFlags(
-      String jsonConfigString, Consumer<LegacyTopLevelFlags.Builder> topLevelFlagAmender) {
+      String jsonConfigString, Consumer<TopLevelFlagsBuilder<?>> topLevelFlagAmender) {
     LegacyTopLevelFlags.Builder builder = LegacyTopLevelFlags.builder();
 
     builder.setJsonSource(jsonConfigString);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyRewritingFlags.java
index 61a2eb8..9a38ff1a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyRewritingFlags.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
@@ -69,13 +68,6 @@
         ImmutableSet.of());
   }
 
-  public static LegacyRewritingFlags withOnlyRewritePrefixForTesting(
-      Map<String, String> prefix, InternalOptions options) {
-    Builder builder = builder(options.dexItemFactory(), options.reporter, Origin.unknown());
-    prefix.forEach(builder::putRewritePrefix);
-    return builder.build();
-  }
-
   public static Builder builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
     return new Builder(dexItemFactory, reporter, origin);
   }
@@ -127,6 +119,12 @@
     return wrapperConversions;
   }
 
+  public boolean isEmpty() {
+    return rewritePrefix.isEmpty()
+        && emulateLibraryInterface.isEmpty()
+        && retargetCoreLibMember.isEmpty();
+  }
+
   public static class Builder {
 
     private final DexItemFactory factory;
@@ -172,14 +170,16 @@
       this.factory = factory;
       this.reporter = reporter;
       this.origin = origin;
-      this.rewritePrefix = rewritePrefix;
-      this.emulateLibraryInterface = emulateLibraryInterface;
-      this.retargetCoreLibMember = retargetCoreLibMember;
-      this.backportCoreLibraryMember = backportCoreLibraryMember;
-      this.customConversions = customConversions;
-      this.dontRewriteInvocation = dontRewriteInvocation;
-      this.dontRetargetLibMember = dontRetargetLibMember;
-      this.wrapperConversions = wrapperConversions;
+      this.rewritePrefix = new HashMap<>(rewritePrefix);
+      this.emulateLibraryInterface = new IdentityHashMap<>(emulateLibraryInterface);
+      this.retargetCoreLibMember = new IdentityHashMap<>(retargetCoreLibMember);
+      this.backportCoreLibraryMember = new IdentityHashMap<>(backportCoreLibraryMember);
+      this.customConversions = new IdentityHashMap<>(customConversions);
+      this.dontRewriteInvocation = new ArrayList<>(dontRewriteInvocation);
+      this.dontRetargetLibMember = Sets.newIdentityHashSet();
+      this.dontRetargetLibMember.addAll(dontRetargetLibMember);
+      this.wrapperConversions = Sets.newIdentityHashSet();
+      this.wrapperConversions.addAll(wrapperConversions);
     }
 
     // Utility to set values. Currently assumes the key is fresh.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java
index 8e0293b..59a3733 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyTopLevelFlags.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification;
 
+import com.android.tools.r8.ir.desugar.desugaredlibrary.TopLevelFlagsBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
@@ -90,7 +91,7 @@
     return extraKeepRules;
   }
 
-  public static class Builder {
+  public static class Builder implements TopLevelFlagsBuilder<Builder> {
 
     private AndroidApiLevel requiredCompilationAPILevel;
     private String synthesizedLibraryClassesPackagePrefix =
@@ -122,6 +123,7 @@
       return this;
     }
 
+    @Override
     public Builder setSupportAllCallbacksFromLibrary(boolean supportAllCallbacksFromLibrary) {
       this.supportAllCallbacksFromLibrary = supportAllCallbacksFromLibrary;
       return this;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index adc507f..d578eed 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.List;
 import java.util.Map;
@@ -98,8 +99,8 @@
     return rewritingFlags.getEmulatedVirtualRetargetThroughEmulatedInterface();
   }
 
-  public void forEachRetargetHolder(Consumer<DexType> consumer) {
-    rewritingFlags.forEachRetargetHolder(consumer);
+  public void forEachRetargetMethod(Consumer<DexMethod> consumer) {
+    rewritingFlags.forEachRetargetMethod(consumer);
   }
 
   public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
@@ -135,6 +136,10 @@
     return rewritingFlags.getCustomConversions();
   }
 
+  public Map<DexMethod, MethodAccessFlags> getAmendLibraryMethods() {
+    return rewritingFlags.getAmendLibraryMethod();
+  }
+
   public boolean hasRetargeting() {
     return rewritingFlags.hasRetargeting();
   }
@@ -166,4 +171,8 @@
     }
     return false;
   }
+
+  public AndroidApiLevel getRequiredCompilationApiLevel() {
+    return topLevelFlags.getRequiredCompilationAPILevel();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index f34634d..00a4747 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -33,7 +34,8 @@
       Map<DexType, List<DexMethod>> wrappers,
       Map<DexType, DexType> legacyBackport,
       Set<DexType> dontRetarget,
-      Map<DexType, CustomConversionDescriptor> customConversions) {
+      Map<DexType, CustomConversionDescriptor> customConversions,
+      Map<DexMethod, MethodAccessFlags> amendLibraryMethods) {
     this.rewriteType = rewriteType;
     this.rewriteDerivedTypeOnly = rewriteDerivedTypeOnly;
     this.staticRetarget = staticRetarget;
@@ -46,6 +48,7 @@
     this.legacyBackport = legacyBackport;
     this.dontRetarget = dontRetarget;
     this.customConversions = customConversions;
+    this.amendLibraryMethod = amendLibraryMethods;
   }
 
   // Rewrites all the references to the keys as well as synthetic types derived from any key.
@@ -79,6 +82,7 @@
   private final Map<DexType, DexType> legacyBackport;
   private final Set<DexType> dontRetarget;
   private final Map<DexType, CustomConversionDescriptor> customConversions;
+  private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
 
   public Map<DexType, DexType> getRewriteType() {
     return rewriteType;
@@ -104,10 +108,10 @@
     return emulatedVirtualRetargetThroughEmulatedInterface;
   }
 
-  public void forEachRetargetHolder(Consumer<DexType> consumer) {
-    staticRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
-    nonEmulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
-    emulatedVirtualRetarget.keySet().forEach(m -> consumer.accept(m.getHolderType()));
+  public void forEachRetargetMethod(Consumer<DexMethod> consumer) {
+    staticRetarget.keySet().forEach(consumer);
+    nonEmulatedVirtualRetarget.keySet().forEach(consumer);
+    emulatedVirtualRetarget.keySet().forEach(consumer);
   }
 
   public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
@@ -138,6 +142,10 @@
     return customConversions;
   }
 
+  public Map<DexMethod, MethodAccessFlags> getAmendLibraryMethod() {
+    return amendLibraryMethod;
+  }
+
   public boolean hasRetargeting() {
     return !staticRetarget.isEmpty()
         || !nonEmulatedVirtualRetarget.isEmpty()
@@ -182,6 +190,8 @@
     private final ImmutableSet.Builder<DexType> dontRetarget = ImmutableSet.builder();
     private final ImmutableMap.Builder<DexType, CustomConversionDescriptor> customConversions =
         ImmutableMap.builder();
+    private final ImmutableMap.Builder<DexMethod, MethodAccessFlags> amendLibraryMethod =
+        ImmutableMap.builder();
 
     public void rewriteType(DexType src, DexType target) {
       assert src != null;
@@ -231,6 +241,10 @@
       customConversions.put(src, descriptor);
     }
 
+    public void amendLibraryMethod(DexMethod missingReference, MethodAccessFlags flags) {
+      amendLibraryMethod.put(missingReference, flags);
+    }
+
     public DexType getRewrittenType(DexType type) {
       return rewriteType.get(type);
     }
@@ -247,7 +261,8 @@
           wrappers.build(),
           legacyBackport.build(),
           dontRetarget.build(),
-          customConversions.build());
+          customConversions.build(),
+          amendLibraryMethod.build());
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index 73dd908..a4b984f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -140,13 +140,11 @@
     AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     MethodResolutionResult resolutionResult =
         appInfo.resolveMethod(invokedMethod, cfInvoke.isInterface());
-    // We are required to use the invokedMethod if it does not resolve due to the rewriting of
-    // private methods absent from the library.
-    DexMethod singleTarget =
-        resolutionResult.isSingleResolution()
-            ? resolutionResult.getSingleTarget().getReference()
-            : invokedMethod;
-    assert singleTarget != null;
+    if (!resolutionResult.isSingleResolution()) {
+      return NO_REWRITING;
+    }
+    assert resolutionResult.getSingleTarget() != null;
+    DexMethod singleTarget = resolutionResult.getSingleTarget().getReference();
     if (cfInvoke.isInvokeStatic()) {
       DexMethod retarget = staticRetarget.get(singleTarget);
       return retarget == null ? NO_REWRITING : ensureInvokeRetargetingResult(retarget);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
index 4be8ef0..198fbc2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -43,7 +43,7 @@
       AppInfoWithClassHierarchy appInfo,
       MachineRewritingFlags.Builder builder,
       BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
-    Map<DexType, DexType> emulateInterfaces = rewritingFlags.getEmulateLibraryInterface();
+    Map<DexType, DexType> emulateInterfaces = rewritingFlags.getEmulatedInterfaces();
     Set<DexMethod> dontRewriteInvocation = rewritingFlags.getDontRewriteInvocation();
     processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces);
     for (DexType itf : emulateInterfaces.keySet()) {
@@ -73,7 +73,7 @@
         appInfo
             .dexItemFactory()
             .createMethod(
-                rewritingFlags.getEmulateLibraryInterface().get(method.getHolderType()),
+                rewritingFlags.getEmulatedInterfaces().get(method.getHolderType()),
                 method.getProto(),
                 method.getName());
     DerivedMethod interfaceMethod = new DerivedMethod(itfDexMethod);
@@ -93,7 +93,7 @@
     List<DexType> subInterfaces = emulatedInterfaceHierarchy.get(method.getHolderType());
     LinkedHashMap<DexType, DerivedMethod> extraDispatchCases = new LinkedHashMap<>();
     // Retarget core lib emulated dispatch handled as part of emulated interface dispatch.
-    Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetCoreLibMember();
+    Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetMethod();
     for (DexMethod retarget : retargetCoreLibMember.keySet()) {
       if (retarget.match(method)) {
         DexClass inClass = appInfo.definitionFor(retarget.getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index 5704f3d..55ac78c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
-import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -25,49 +24,40 @@
   private final MachineRewritingFlags.Builder builder;
   private final String synthesizedPrefix;
   private final Map<DexString, DexString> descriptorPrefix;
-  private final Map<DexType, DexType> reverse = new IdentityHashMap<>();
+  private final Map<DexString, Map<DexString, DexString>> descriptorDifferentPrefix;
   private final Set<DexString> usedPrefix = Sets.newIdentityHashSet();
 
   public HumanToMachinePrefixConverter(
       AppInfoWithClassHierarchy appInfo,
       MachineRewritingFlags.Builder builder,
       String synthesizedPrefix,
-      Map<String, String> descriptorPrefix) {
+      HumanRewritingFlags rewritingFlags) {
     this.appInfo = appInfo;
     this.builder = builder;
     this.synthesizedPrefix = synthesizedPrefix;
-    this.descriptorPrefix = convertRewritePrefix(descriptorPrefix);
+    this.descriptorPrefix = convertRewritePrefix(rewritingFlags.getRewritePrefix());
+    this.descriptorDifferentPrefix =
+        convertRewriteDifferentPrefix(rewritingFlags.getRewriteDerivedPrefix());
   }
 
   public void convertPrefixFlags(
       HumanRewritingFlags rewritingFlags, BiConsumer<String, Set<DexString>> warnConsumer) {
     rewriteClasses();
-    rewriteValues(rewritingFlags.getRetargetCoreLibMember());
+    rewriteValues(rewritingFlags.getRetargetMethod());
     rewriteValues(rewritingFlags.getCustomConversions());
-    rewriteEmulatedInterface(rewritingFlags.getEmulateLibraryInterface());
-    rewriteRetargetKeys(rewritingFlags.getRetargetCoreLibMember());
-    rewriteReverse();
+    rewriteEmulatedInterface(rewritingFlags.getEmulatedInterfaces());
+    rewriteRetargetKeys(rewritingFlags.getRetargetMethod());
     warnIfUnusedPrefix(warnConsumer);
   }
 
   private void warnIfUnusedPrefix(BiConsumer<String, Set<DexString>> warnConsumer) {
     Set<DexString> prefixes = Sets.newIdentityHashSet();
     prefixes.addAll(descriptorPrefix.keySet());
+    prefixes.addAll(descriptorDifferentPrefix.keySet());
     prefixes.removeAll(usedPrefix);
     warnConsumer.accept("The following prefixes do not match any type: ", prefixes);
   }
 
-  // For custom conversions, this is responsible in rewriting backward.
-  private void rewriteReverse() {
-    reverse.forEach(
-        (rewrittenType, type) -> {
-          DexType chainType = rewrittenType(rewrittenType);
-          if (chainType != null) {
-            builder.rewriteType(rewrittenType, chainType);
-          }
-        });
-  }
-
   public DexType convertJavaNameToDesugaredLibrary(DexType type) {
     String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(synthesizedPrefix);
     String interfaceType = type.toString();
@@ -90,11 +80,6 @@
     emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
   }
 
-  private void rewriteType(DexType type, DexType rewrittenType) {
-    builder.rewriteType(type, rewrittenType);
-    reverse.put(rewrittenType, type);
-  }
-
   private void rewriteValues(
       Map<?, DexType> flags) {
     for (DexType type : flags.values()) {
@@ -104,46 +89,76 @@
 
   private void rewriteClasses() {
     for (DexClass clazz : appInfo.app().asDirect().libraryClasses()) {
-      rewriteClass(clazz);
+      if (clazz.toString().contains("MonthDay")) {
+        clazz.toString();
+      }
+      registerType(clazz.type);
+      registerDifferentType(clazz.type);
     }
     for (DexClass clazz : appInfo.classes()) {
-      rewriteClass(clazz);
-    }
-  }
-
-  private void rewriteClass(DexClass clazz) {
-    registerType(clazz.type);
-    // We allow missing referenced types for the work-in-progress desugaring.
-    if (clazz.superType != null) {
-      registerType(clazz.superType);
-    }
-    clazz.interfaces.forEach(this::registerType);
-    if (clazz.getInnerClasses() != null) {
-      clazz.getInnerClasses().forEach(attr -> attr.forEachType(this::registerType));
+      registerType(clazz.type);
+      registerDifferentType(clazz.type);
     }
   }
 
   private void registerType(DexType type) {
     DexType rewrittenType = rewrittenType(type);
     if (rewrittenType != null) {
-      rewriteType(type, rewrittenType);
+      builder.rewriteType(type, rewrittenType);
     }
   }
 
-  private DexType rewrittenType(DexType type) {
+  private void registerDifferentType(DexType type) {
+    DexString prefix = prefixMatching(type, descriptorDifferentPrefix.keySet());
+    if (prefix == null) {
+      return;
+    }
+    descriptorDifferentPrefix
+        .get(prefix)
+        .forEach(
+            (k, v) -> {
+              DexString typeDescriptor =
+                  type.descriptor.withNewPrefix(prefix, k, appInfo.dexItemFactory());
+              DexString rewrittenTypeDescriptor =
+                  type.descriptor.withNewPrefix(prefix, v, appInfo.dexItemFactory());
+              builder.rewriteType(
+                  appInfo.dexItemFactory().createType(typeDescriptor),
+                  appInfo.dexItemFactory().createType(rewrittenTypeDescriptor));
+            });
+    usedPrefix.add(prefix);
+  }
+
+  private DexString prefixMatching(DexType type, Set<DexString> prefixes) {
     DexString prefixToMatch = type.descriptor.withoutArray(appInfo.dexItemFactory());
-    for (DexString prefix : descriptorPrefix.keySet()) {
+    for (DexString prefix : prefixes) {
       if (prefixToMatch.startsWith(prefix)) {
-        DexString rewrittenTypeDescriptor =
-            type.descriptor.withNewPrefix(
-                prefix, descriptorPrefix.get(prefix), appInfo.dexItemFactory());
-        usedPrefix.add(prefix);
-        return appInfo.dexItemFactory().createType(rewrittenTypeDescriptor);
+        return prefix;
       }
     }
     return null;
   }
 
+  private DexType rewrittenType(DexType type) {
+    DexString prefix = prefixMatching(type, descriptorPrefix.keySet());
+    if (prefix == null) {
+      return null;
+    }
+    DexString rewrittenTypeDescriptor =
+        type.descriptor.withNewPrefix(
+            prefix, descriptorPrefix.get(prefix), appInfo.dexItemFactory());
+    usedPrefix.add(prefix);
+    return appInfo.dexItemFactory().createType(rewrittenTypeDescriptor);
+  }
+
+  private ImmutableMap<DexString, Map<DexString, DexString>> convertRewriteDifferentPrefix(
+      Map<String, Map<String, String>> rewriteDerivedPrefix) {
+    ImmutableMap.Builder<DexString, Map<DexString, DexString>> mapBuilder = ImmutableMap.builder();
+    for (String key : rewriteDerivedPrefix.keySet()) {
+      mapBuilder.put(toDescriptorPrefix(key), convertRewritePrefix(rewriteDerivedPrefix.get(key)));
+    }
+    return mapBuilder.build();
+  }
+
   private ImmutableMap<DexString, DexString> convertRewritePrefix(
       Map<String, String> rewritePrefix) {
     ImmutableMap.Builder<DexString, DexString> mapBuilder = ImmutableMap.builder();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index e153c70..6268125 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -41,7 +41,7 @@
       MachineRewritingFlags.Builder builder,
       BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
     rewritingFlags
-        .getRetargetCoreLibMember()
+        .getRetargetMethod()
         .forEach(
             (method, type) ->
                 convertRetargetCoreLibMemberFlag(builder, rewritingFlags, method, type));
@@ -55,14 +55,8 @@
       DexType type) {
     DexClass holder = appInfo.definitionFor(method.holder);
     DexEncodedMethod foundMethod = holder.lookupMethod(method);
-    if (foundMethod == null && method.getName().toString().equals("deepEquals0")) {
-      // TODO(b/184026720): Temporary work-around (the method is missing).
-      DexMethod dest = method.withHolder(type, appInfo.dexItemFactory());
-      builder.putStaticRetarget(method, dest);
-      return;
-    }
     if (foundMethod == null) {
-      missingMethods.add(foundMethod.getReference());
+      missingMethods.add(method);
       return;
     }
     if (foundMethod.isStatic()) {
@@ -127,8 +121,7 @@
       AppInfoWithClassHierarchy appInfo,
       HumanRewritingFlags humanRewritingFlags) {
     // Answers true if this method is already managed through emulated interface dispatch.
-    Map<DexType, DexType> emulateLibraryInterface =
-        humanRewritingFlags.getEmulateLibraryInterface();
+    Map<DexType, DexType> emulateLibraryInterface = humanRewritingFlags.getEmulatedInterfaces();
     if (emulateLibraryInterface.isEmpty()) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index c668858..51516d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -16,6 +16,8 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
@@ -27,6 +29,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -37,6 +40,7 @@
 public class HumanToMachineSpecificationConverter {
 
   private AppView<?> appView;
+  private final Set<DexType> missingCustomConversions = Sets.newIdentityHashSet();
 
   public MachineDesugaredLibrarySpecification convert(
       HumanDesugaredLibrarySpecification humanSpec,
@@ -77,7 +81,10 @@
       throws IOException {
     DexApplication app = readApp(inputApp, options);
     appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
-    LibraryValidator.validate(app, humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel());
+    LibraryValidator.validate(
+        app,
+        humanSpec.isLibraryCompilation(),
+        humanSpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     MachineRewritingFlags machineRewritingFlags =
         convertRewritingFlags(
             humanSpec.getSynthesizedLibraryClassesPackagePrefix(), humanSpec.getRewritingFlags());
@@ -100,12 +107,13 @@
       String synthesizedPrefix, HumanRewritingFlags rewritingFlags) {
     AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
+    DesugaredLibraryAmender.run(appView, rewritingFlags.getAmendLibraryMethod());
+    rewritingFlags.getAmendLibraryMethod().forEach(builder::amendLibraryMethod);
     new HumanToMachineRetargetConverter(appInfo)
         .convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
     new HumanToMachineEmulatedInterfaceConverter(appInfo)
         .convertEmulatedInterfaces(rewritingFlags, appInfo, builder, this::warnMissingReferences);
-    new HumanToMachinePrefixConverter(
-            appInfo, builder, synthesizedPrefix, rewritingFlags.getRewritePrefix())
+    new HumanToMachinePrefixConverter(appInfo, builder, synthesizedPrefix, rewritingFlags)
         .convertPrefixFlags(rewritingFlags, this::warnMissingDexString);
     new HumanToMachineWrapperConverter(appInfo)
         .convertWrappers(rewritingFlags, builder, this::warnMissingReferences);
@@ -114,8 +122,10 @@
         .forEach(
             (type, conversionType) ->
                 convertCustomConversion(appInfo, builder, type, conversionType));
-    rewritingFlags.getDontRetargetLibMember().forEach(builder::addDontRetarget);
-    rewritingFlags.getBackportCoreLibraryMember().forEach(builder::putLegacyBackport);
+    warnMissingReferences(
+        "Cannot register custom conversion due to missing type: ", missingCustomConversions);
+    rewritingFlags.getDontRetarget().forEach(builder::addDontRetarget);
+    rewritingFlags.getLegacyBackport().forEach(builder::putLegacyBackport);
     return builder.build();
   }
 
@@ -125,6 +135,10 @@
       DexType type,
       DexType conversionType) {
     DexType rewrittenType = builder.getRewrittenType(type);
+    if (rewrittenType == null) {
+      missingCustomConversions.add(type);
+      return;
+    }
     DexProto fromProto = appInfo.dexItemFactory().createProto(rewrittenType, type);
     DexMethod fromMethod =
         appInfo
@@ -141,7 +155,11 @@
   private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
     ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
-    return applicationReader.read(executorService).toDirect();
+    assert !options.ignoreJavaLibraryOverride;
+    options.ignoreJavaLibraryOverride = true;
+    DirectMappedDexApplication app = applicationReader.read(executorService).toDirect();
+    options.ignoreJavaLibraryOverride = false;
+    return app;
   }
 
   void warnMissingReferences(String message, Set<? extends DexReference> missingReferences) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index c4884a5..3a2c990 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -16,6 +17,7 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
@@ -34,6 +36,7 @@
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import java.io.IOException;
@@ -45,6 +48,7 @@
 
 public class LegacyToHumanSpecificationConverter {
 
+  private static final String wrapperPrefix = "__wrapper__.";
   private AndroidApiLevel legacyHackLevel = AndroidApiLevel.N_MR1;
 
   public void convertAllAPILevels(
@@ -57,8 +61,6 @@
             .parseMultiLevelConfiguration(inputSpecification);
     MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
         convertAllAPILevels(legacySpec, androidLib, options);
-    MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.deduplicateFlags(
-        humanSpec, options.dexItemFactory(), options.reporter);
     MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(humanSpec, output);
   }
 
@@ -70,7 +72,6 @@
     Origin origin = legacySpec.getOrigin();
     AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
     DexApplication app = readApp(androidApp, options);
-    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     Int2ObjectArrayMap<HumanRewritingFlags> commonFlags =
         convertRewritingFlagMap(legacySpec.getCommonFlags(), app, origin);
@@ -81,8 +82,12 @@
 
     legacyLibraryFlagHacks(libraryFlags, app, origin);
 
-    return new MultiAPILevelHumanDesugaredLibrarySpecification(
-        origin, humanTopLevelFlags, commonFlags, libraryFlags, programFlags);
+    MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
+        new MultiAPILevelHumanDesugaredLibrarySpecification(
+            origin, humanTopLevelFlags, commonFlags, libraryFlags, programFlags);
+    MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.deduplicateFlags(
+        humanSpec, options.reporter);
+    return humanSpec;
   }
 
   public HumanDesugaredLibrarySpecification convert(
@@ -108,7 +113,10 @@
       LegacyDesugaredLibrarySpecification legacySpec, AndroidApp inputApp, InternalOptions options)
       throws IOException {
     DexApplication app = readApp(inputApp, options);
-    LibraryValidator.validate(app, legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
+    LibraryValidator.validate(
+        app,
+        legacySpec.isLibraryCompilation(),
+        legacySpec.getTopLevelFlags().getRequiredCompilationAPILevel());
     HumanTopLevelFlags humanTopLevelFlags = convertTopLevelFlags(legacySpec.getTopLevelFlags());
     // The origin is not maintained in non multi-level specifications.
     // It should not matter since the origin is used to report invalid specifications, and
@@ -120,7 +128,7 @@
     if (options.getMinApiLevel().isLessThanOrEqualTo(legacyHackLevel)
         && legacySpec.isLibraryCompilation()) {
       HumanRewritingFlags.Builder builder =
-          humanRewritingFlags.newBuilder(app.dexItemFactory(), app.options.reporter, origin);
+          humanRewritingFlags.newBuilder(app.options.reporter, origin);
       legacyLibraryFlagHacks(app.dexItemFactory(), builder);
       humanRewritingFlags = builder.build();
     }
@@ -132,8 +140,11 @@
       Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) {
     int level = legacyHackLevel.getLevel();
     HumanRewritingFlags humanRewritingFlags = libraryFlags.get(level);
+    if (humanRewritingFlags == null) {
+      return;
+    }
     HumanRewritingFlags.Builder builder =
-        humanRewritingFlags.newBuilder(app.dexItemFactory(), app.options.reporter, origin);
+        humanRewritingFlags.newBuilder(app.options.reporter, origin);
     legacyLibraryFlagHacks(app.dexItemFactory(), builder);
     libraryFlags.put(level, builder.build());
   }
@@ -150,7 +161,12 @@
     DexMethod source =
         itemFactory.createMethod(itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
     DexType target = itemFactory.createType("Ljava/util/DesugarArrays;");
-    builder.putRetargetCoreLibMember(source, target);
+    builder.retargetMethod(source, target);
+
+    builder.amendLibraryMethod(
+        source,
+        MethodAccessFlags.fromSharedAccessFlags(
+            Constants.ACC_PRIVATE | Constants.ACC_STATIC, false));
 
     // TODO(b/181629049): This is only a workaround rewriting invokes of
     //  j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
@@ -161,7 +177,7 @@
             itemFactory.createType("Ljava/time/ZoneId;"));
     source = itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
     target = itemFactory.createType("Ljava/util/DesugarTimeZone;");
-    builder.putRetargetCoreLibMember(source, target);
+    builder.retargetMethod(source, target);
   }
 
   private DexApplication readApp(AndroidApp inputApp, InternalOptions options) throws IOException {
@@ -179,12 +195,13 @@
 
   private HumanRewritingFlags convertRewritingFlags(
       LegacyRewritingFlags flags, DexApplication app, Origin origin) {
-    HumanRewritingFlags.Builder builder =
-        HumanRewritingFlags.builder(app.dexItemFactory(), app.options.reporter, origin);
+    HumanRewritingFlags.Builder builder = HumanRewritingFlags.builder(app.options.reporter, origin);
 
-    flags.getRewritePrefix().forEach(builder::putRewritePrefix);
-    flags.getEmulateLibraryInterface().forEach(builder::putEmulateLibraryInterface);
-    flags.getBackportCoreLibraryMember().forEach(builder::putBackportCoreLibraryMember);
+    flags
+        .getRewritePrefix()
+        .forEach((prefix, rewritten) -> rewritePrefix(builder, prefix, rewritten));
+    flags.getEmulateLibraryInterface().forEach(builder::putEmulatedInterface);
+    flags.getBackportCoreLibraryMember().forEach(builder::putLegacyBackport);
     flags.getCustomConversions().forEach(builder::putCustomConversion);
     flags.getDontRetargetLibMember().forEach(builder::addDontRetargetLibMember);
     flags.getWrapperConversions().forEach(builder::addWrapperConversion);
@@ -199,6 +216,28 @@
     return builder.build();
   }
 
+  private void rewritePrefix(HumanRewritingFlags.Builder builder, String prefix, String rewritten) {
+    // Legacy hacks: The human specification matches on class' types so we need different
+    // rewritings.
+    if (prefix.startsWith("j$")) {
+      assert rewritten.startsWith("java");
+      builder.putRewriteDerivedPrefix(rewritten, prefix, rewritten);
+      return;
+    }
+    if (prefix.equals(wrapperPrefix)) {
+      // We hard code here this applies to java.nio and java.io only.
+      ImmutableMap<String, String> map =
+          ImmutableMap.of("java.nio.", "j$.nio.", "java.io.", "j$.io.");
+      map.forEach(
+          (k, v) -> {
+            builder.putRewriteDerivedPrefix(k, wrapperPrefix + k, k);
+            builder.putRewriteDerivedPrefix(k, wrapperPrefix + v, v);
+          });
+      return;
+    }
+    builder.putRewritePrefix(prefix, rewritten);
+  }
+
   private void convertDontRewriteInvocation(
       HumanRewritingFlags.Builder builder, DexApplication app, Pair<DexType, DexString> pair) {
     DexClass dexClass = app.definitionFor(pair.getFirst());
@@ -220,7 +259,7 @@
           assert dexClass != null;
           List<DexClassAndMethod> methodsWithName = findMethodsWithName(name, dexClass);
           for (DexClassAndMethod dexClassAndMethod : methodsWithName) {
-            builder.putRetargetCoreLibMember(dexClassAndMethod.getReference(), rewrittenType);
+            builder.retargetMethod(dexClassAndMethod.getReference(), rewrittenType);
           }
         });
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
index b415c354..ef19eb6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
@@ -11,9 +11,16 @@
 
 public class LibraryValidator {
 
-  // Estimates if the library passed is at the expected minimum level, if it is not, raise
-  // a warning.
-  public static void validate(DexApplication app, AndroidApiLevel requiredCompilationAPILevel) {
+  /**
+   * In program compilation, The LibraryValidator estimates if the library passed is at the expected
+   * minimum level, if it is not, raises a warning.
+   */
+  public static void validate(
+      DexApplication app, boolean libraryCompilation, AndroidApiLevel requiredCompilationAPILevel) {
+    if (libraryCompilation) {
+      // In library compilation, the classes are passed as program classes and are always found.
+      return;
+    }
     DexType levelType;
     if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.O)) {
       levelType = app.dexItemFactory.createType("Ljava/time/LocalTime;");
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 3aec49b..06f2d76 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -420,7 +420,7 @@
     }
 
     @Override
-    protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+    public DexMethod getNextMethodSignature(DexMethod method) {
       return newMethodSignatures.getRepresentativeValueOrDefault(
           method, extraNewMethodSignatures.getRepresentativeValueOrDefault(method, method));
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java
deleted file mode 100644
index 8f9421d..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.optimize;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-public class ArgumentRemovalUtils {
-
-  // Returns true if this method is pinned from the perspective of optimizations that attempt to
-  // remove method arguments.
-  public static boolean isPinned(DexEncodedMethod method, AppView<AppInfoWithLiveness> appView) {
-    return appView.appInfo().isPinned(method.getReference())
-        || appView.appInfo().isBootstrapMethod(method.getReference())
-        || appView.appInfo().isFailedResolutionTarget(method.getReference())
-        || appView.appInfo().isMethodTargetedByInvokeDynamic(method.getReference())
-        || method.accessFlags.isNative();
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
index 00867f4..a980c52 100644
--- a/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/FieldRebindingIdentityLens.java
@@ -100,6 +100,11 @@
   }
 
   @Override
+  public DexMethod getNextMethodSignature(DexMethod method) {
+    return method;
+  }
+
+  @Override
   public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
       DexMethod method, GraphLens codeLens) {
     if (this == codeLens) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index b77fbd1..ebf61af 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -123,6 +123,11 @@
   }
 
   @Override
+  public DexMethod getNextMethodSignature(DexMethod method) {
+    return method;
+  }
+
+  @Override
   public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
       DexMethod method, GraphLens codeLens) {
     if (this == codeLens) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index edba428..5065a75 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -138,6 +138,11 @@
     return method;
   }
 
+  @Override
+  public DexMethod getNextMethodSignature(DexMethod method) {
+    return method;
+  }
+
   public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
       AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index e72be3d..8dd8cb0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -94,7 +94,7 @@
         method -> {
           DexMethod methodReferenceBeforeParameterRemoval = method.getReference();
           DexMethod methodReferenceAfterParameterRemoval =
-              graphLens.internalGetNextMethodSignature(methodReferenceBeforeParameterRemoval);
+              graphLens.getNextMethodSignature(methodReferenceBeforeParameterRemoval);
           if (methodReferenceAfterParameterRemoval == methodReferenceBeforeParameterRemoval
               && !graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
             return method;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index d67718a..958c85b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -98,8 +98,8 @@
   }
 
   @Override
-  public DexMethod internalGetNextMethodSignature(DexMethod method) {
-    return super.internalGetNextMethodSignature(method);
+  public DexMethod getNextMethodSignature(DexMethod method) {
+    return super.getNextMethodSignature(method);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index 6e6e88c..fdf81ab 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -87,7 +87,7 @@
 
             if (graphLens != null) {
               DexMethod rewrittenMethodSignature =
-                  graphLens.internalGetNextMethodSignature(method.getReference());
+                  graphLens.getNextMethodSignature(method.getReference());
               if (graphLens.hasPrototypeChanges(rewrittenMethodSignature)) {
                 assert !appView.appInfo().isNeverReprocessMethod(method);
                 postMethodProcessorBuilder.add(method, currentGraphLens);
@@ -195,7 +195,7 @@
 
       ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
       DexMethod rewrittenMethodReference =
-          graphLens.internalGetNextMethodSignature(resolvedMethod.getReference());
+          graphLens.getNextMethodSignature(resolvedMethod.getReference());
       if (rewrittenMethodReference != resolvedMethod.getReference()
           || graphLens.hasPrototypeChanges(rewrittenMethodReference)) {
         markAffected();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index c30afaf..24d6e46 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.graph.proto.RemovedArgumentInfo;
+import com.android.tools.r8.graph.proto.RemovedReceiverInfo;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.graph.proto.RewrittenTypeInfo;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -419,7 +420,6 @@
       return appView.getKeepInfo(method).isParameterRemovalAllowed(options)
           && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
           && !appView.appInfo().isBootstrapMethod(method)
-          && !appView.appInfo().isMethodTargetedByInvokeDynamic(method)
           && !interfaceDispatchOutsideProgram.contains(method);
     }
 
@@ -1022,11 +1022,7 @@
           && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
           && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, 0)) {
         parameterChangesBuilder.addArgumentInfo(
-            0,
-            RemovedArgumentInfo.builder()
-                .setCheckNullOrZero(true)
-                .setType(method.getHolderType())
-                .build());
+            0, RemovedReceiverInfo.Builder.create().setType(method.getHolderType()).build());
       }
 
       CallSiteOptimizationInfo optimizationInfo = method.getOptimizationInfo().getArgumentInfos();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
index c277bb7..28aa563 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorUnoptimizableMethods.java
@@ -67,7 +67,6 @@
     AppInfoWithLiveness appInfo = appView.appInfo();
     InternalOptions options = appView.options();
     return method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
-        || appInfo.isMethodTargetedByInvokeDynamic(method)
         || !appInfo.getKeepInfo().getMethodInfo(method).isArgumentPropagationAllowed(options);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
index e0341ae..07ffe61 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
@@ -76,8 +76,7 @@
 
       ArgumentInfo receiverArgumentInfo =
           lookup.getPrototypeChanges().getArgumentInfoCollection().getArgumentInfo(0);
-      if (!receiverArgumentInfo.isRemovedArgumentInfo()
-          || !receiverArgumentInfo.asRemovedArgumentInfo().isCheckNullOrZeroSet()) {
+      if (!receiverArgumentInfo.isRemovedReceiverInfo()) {
         return;
       }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
index 34d8967..cb44578 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
@@ -22,8 +22,7 @@
     if (!appView.getKeepInfo(method).isUnusedArgumentOptimizationAllowed(options)) {
       return false;
     }
-    return method.getDefinition().isLibraryMethodOverride().isFalse()
-        && !appView.appInfoWithLiveness().isMethodTargetedByInvokeDynamic(method);
+    return method.getDefinition().isLibraryMethodOverride().isFalse();
   }
 
   public static boolean canRemoveUnusedParameter(
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
index 9827d9a..d47ecd6 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -29,7 +29,7 @@
   @Override
   public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
     DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
-    return bridgeToHoistedBridgeMap.getOrDefault(renamedMethod, renamedMethod);
+    return getNextMethodSignature(renamedMethod);
   }
 
   @Override
@@ -39,6 +39,11 @@
   }
 
   @Override
+  public DexMethod getNextMethodSignature(DexMethod method) {
+    return bridgeToHoistedBridgeMap.getOrDefault(method, method);
+  }
+
+  @Override
   public DexType getOriginalType(DexType type) {
     return getPrevious().getOriginalType(type);
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
index 29ed273..b1237ed 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.MapUtils.ignoreKey;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
@@ -13,14 +14,20 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
 import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
 import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.Collections;
 import java.util.HashMap;
@@ -59,21 +66,39 @@
     LocalReservationState localReservationState = new LocalReservationState();
     ProtoNormalizerGraphLens.Builder lensBuilder = ProtoNormalizerGraphLens.builder(appView);
     for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+      Map<DexMethodSignature, DexMethodSignature> newInstanceInitializerSignatures =
+          computeNewInstanceInitializerSignatures(
+              clazz, localReservationState, globalReservationState);
       clazz
           .getMethodCollection()
           .replaceMethods(
               method -> {
                 DexMethodSignature methodSignature = method.getSignature();
                 DexMethodSignature newMethodSignature =
-                    localReservationState.getNewMethodSignature(
-                        methodSignature, dexItemFactory, globalReservationState);
+                    method.isInstanceInitializer()
+                        ? newInstanceInitializerSignatures.get(methodSignature)
+                        : localReservationState.getAndReserveNewMethodSignature(
+                            methodSignature, dexItemFactory, globalReservationState);
                 if (methodSignature.equals(newMethodSignature)) {
                   return method;
                 }
                 DexMethod newMethodReference = newMethodSignature.withHolder(clazz, dexItemFactory);
-                lensBuilder.recordNewMethodSignature(method, newMethodReference);
-                // TODO(b/195112263): Fixup any optimization info and parameter annotations.
-                return method.toTypeSubstitutedMethod(newMethodReference);
+                RewrittenPrototypeDescription prototypeChanges =
+                    lensBuilder.recordNewMethodSignature(method, newMethodReference);
+                // TODO(b/195112263): Assert that the method does not have any optimization info.
+                //  If/when enabling proto normalization after the final round of tree shaking, this
+                //  should simply clear the optimization info, or replace it by a
+                //  ThrowingMethodOptimizationInfo since we should never use the optimization info
+                //  after this point.
+                return method.toTypeSubstitutedMethod(
+                    newMethodReference,
+                    builder -> {
+                      if (!prototypeChanges.isEmpty()) {
+                        builder
+                            .apply(prototypeChanges.createParameterAnnotationsRemover(method))
+                            .setGenericSignature(MethodTypeSignature.noSignature());
+                      }
+                    });
               });
     }
 
@@ -170,6 +195,90 @@
     }
   }
 
+  Map<DexMethodSignature, DexMethodSignature> computeNewInstanceInitializerSignatures(
+      DexProgramClass clazz,
+      LocalReservationState localReservationState,
+      GlobalReservationState globalReservationState) {
+    // Create a map from new method signatures to old method signatures. This produces a one-to-many
+    // mapping since multiple instance initializers may normalize to the same signature.
+    Map<DexMethodSignature, DexMethodSignatureSet> instanceInitializerCollisions =
+        computeInstanceInitializerCollisions(clazz, localReservationState, globalReservationState);
+
+    // Resolve each collision to ensure that the mapping is one-to-one.
+    resolveInstanceInitializerCollisions(instanceInitializerCollisions);
+
+    // Inverse the one-to-one map to produce a mapping from old method signatures to new method
+    // signatures.
+    return MapUtils.transform(
+        instanceInitializerCollisions,
+        HashMap::new,
+        (newMethodSignature, methodSignatures) -> Iterables.getFirst(methodSignatures, null),
+        (newMethodSignature, methodSignatures) -> newMethodSignature,
+        (newMethodSignature, methodSignature, otherMethodSignature) -> {
+          throw new Unreachable();
+        });
+  }
+
+  private Map<DexMethodSignature, DexMethodSignatureSet> computeInstanceInitializerCollisions(
+      DexProgramClass clazz,
+      LocalReservationState localReservationState,
+      GlobalReservationState globalReservationState) {
+    Map<DexMethodSignature, DexMethodSignatureSet> instanceInitializerCollisions = new HashMap<>();
+    clazz.forEachProgramInstanceInitializer(
+        method -> {
+          DexMethodSignature methodSignature = method.getMethodSignature();
+          DexMethodSignature newMethodSignature =
+              localReservationState.getNewMethodSignature(
+                  methodSignature, dexItemFactory, globalReservationState);
+          instanceInitializerCollisions
+              .computeIfAbsent(newMethodSignature, ignoreKey(DexMethodSignatureSet::create))
+              .add(methodSignature);
+        });
+    return instanceInitializerCollisions;
+  }
+
+  private void resolveInstanceInitializerCollisions(
+      Map<DexMethodSignature, DexMethodSignatureSet> instanceInitializerCollisions) {
+    WorkList<DexMethodSignature> worklist = WorkList.newEqualityWorkList();
+    instanceInitializerCollisions.forEach(
+        (newMethodSignature, methodSignatures) -> {
+          if (methodSignatures.size() > 1) {
+            worklist.addIfNotSeen(newMethodSignature);
+          }
+        });
+
+    while (worklist.hasNext()) {
+      DexMethodSignature newMethodSignature = worklist.removeSeen();
+      DexMethodSignatureSet methodSignatures =
+          instanceInitializerCollisions.get(newMethodSignature);
+      assert methodSignatures.size() > 1;
+
+      // Resolve this conflict in a deterministic way.
+      DexMethodSignature survivor =
+          methodSignatures.contains(newMethodSignature)
+              ? newMethodSignature
+              : IterableUtils.min(methodSignatures, DexMethodSignature::compareTo);
+
+      // Disallow optimizations of all other methods than the `survivor`.
+      for (DexMethodSignature methodSignature : methodSignatures) {
+        if (!methodSignature.equals(survivor)) {
+          DexMethodSignatureSet originalMethodSignaturesForMethodSignature =
+              instanceInitializerCollisions.computeIfAbsent(
+                  methodSignature, ignoreKey(DexMethodSignatureSet::create));
+          originalMethodSignaturesForMethodSignature.add(methodSignature);
+          if (originalMethodSignaturesForMethodSignature.size() > 1) {
+            worklist.addIfNotSeen(methodSignature);
+          }
+        }
+      }
+
+      // Remove all pinned methods from the set of original method signatures stored at
+      // instanceInitializerCollisions.get(newMethodSignature).
+      methodSignatures.clear();
+      methodSignatures.add(survivor);
+    }
+  }
+
   private boolean isUnoptimizable(ProgramMethod method) {
     // TODO(b/195112263): This is incomplete.
     return appView.getKeepInfo(method).isPinned(options)
@@ -226,11 +335,27 @@
     MutableBidirectionalOneToOneMap<DexMethodSignature, DexMethodSignature> newMethodSignatures =
         new BidirectionalOneToOneHashMap<>();
 
-    // TODO: avoid sorting multiple times.
     DexMethodSignature getNewMethodSignature(
         DexMethodSignature methodSignature,
         DexItemFactory dexItemFactory,
         GlobalReservationState globalReservationState) {
+      return internalGetAndReserveNewMethodSignature(
+          methodSignature, dexItemFactory, globalReservationState, false);
+    }
+
+    DexMethodSignature getAndReserveNewMethodSignature(
+        DexMethodSignature methodSignature,
+        DexItemFactory dexItemFactory,
+        GlobalReservationState globalReservationState) {
+      return internalGetAndReserveNewMethodSignature(
+          methodSignature, dexItemFactory, globalReservationState, true);
+    }
+
+    private DexMethodSignature internalGetAndReserveNewMethodSignature(
+        DexMethodSignature methodSignature,
+        DexItemFactory dexItemFactory,
+        GlobalReservationState globalReservationState,
+        boolean reserve) {
       if (globalReservationState.isUnoptimizable(methodSignature)) {
         assert !newMethodSignatures.containsKey(methodSignature);
         return methodSignature;
@@ -254,7 +379,9 @@
           newMethodSignature = newMethodSignature.withName(newMethodName);
         } while (newMethodSignatures.containsValue(newMethodSignature));
       }
-      newMethodSignatures.put(methodSignature, newMethodSignature);
+      if (reserve) {
+        newMethodSignatures.put(methodSignature, newMethodSignature);
+      }
       return newMethodSignature;
     }
   }
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 89d95d6..bfeef49 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
@@ -62,20 +62,26 @@
 
   @Override
   public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+    if (this == applied) {
+      return originalMethod;
+    }
     return newMethodSignatures.getOrDefault(originalMethod, originalMethod);
   }
 
   @Override
   public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
       DexMethod method, GraphLens codeLens) {
+    if (this == codeLens) {
+      return RewrittenPrototypeDescription.none();
+    }
     DexMethod previousMethodSignature = getPreviousMethodSignature(method);
     RewrittenPrototypeDescription previousPrototypeChanges =
         getPrevious().lookupPrototypeChangesForMethodDefinition(previousMethodSignature);
     if (previousMethodSignature == method) {
       return previousPrototypeChanges;
     }
-    assert prototypeChanges.containsKey(method);
-    return previousPrototypeChanges.combine(prototypeChanges.get(method));
+    return previousPrototypeChanges.combine(
+        prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none()));
   }
 
   @Override
@@ -115,6 +121,11 @@
     return newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
   }
 
+  @Override
+  public DexMethod getNextMethodSignature(DexMethod method) {
+    return newMethodSignatures.getOrDefault(method, method);
+  }
+
   public static class Builder {
 
     private final AppView<AppInfoWithLiveness> appView;
@@ -127,11 +138,17 @@
       this.appView = appView;
     }
 
-    public Builder recordNewMethodSignature(DexEncodedMethod method, DexMethod newMethodSignature) {
+    public RewrittenPrototypeDescription recordNewMethodSignature(
+        DexEncodedMethod method, DexMethod newMethodSignature) {
       assert method.getReference() != newMethodSignature;
-      prototypeChanges.put(newMethodSignature, computePrototypeChanges(method, newMethodSignature));
       newMethodSignatures.put(method.getReference(), newMethodSignature);
-      return this;
+      if (!method.getParameters().equals(newMethodSignature.getParameters())) {
+        RewrittenPrototypeDescription prototypeChangesForMethod =
+            computePrototypeChanges(method, newMethodSignature);
+        prototypeChanges.put(newMethodSignature, prototypeChangesForMethod);
+        return prototypeChangesForMethod;
+      }
+      return RewrittenPrototypeDescription.none();
     }
 
     // TODO(b/195112263): Canonicalize the permutation maps.
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 8216777..7464171 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -112,8 +112,6 @@
    */
   private final Set<DexMethod> bootstrapMethods;
 
-  /** Set of methods that are the immediate target of an invoke-dynamic. */
-  private final Set<DexMethod> methodsTargetedByInvokeDynamic;
   /** Set of virtual methods that are the immediate target of an invoke-direct. */
   private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect;
   /**
@@ -210,7 +208,6 @@
       Set<DexMethod> failedMethodResolutionTargets,
       Set<DexField> failedFieldResolutionTargets,
       Set<DexMethod> bootstrapMethods,
-      Set<DexMethod> methodsTargetedByInvokeDynamic,
       Set<DexMethod> virtualMethodsTargetedByInvokeDirect,
       Set<DexMethod> liveMethods,
       FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
@@ -245,7 +242,6 @@
     this.failedMethodResolutionTargets = failedMethodResolutionTargets;
     this.failedFieldResolutionTargets = failedFieldResolutionTargets;
     this.bootstrapMethods = bootstrapMethods;
-    this.methodsTargetedByInvokeDynamic = methodsTargetedByInvokeDynamic;
     this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
     this.liveMethods = liveMethods;
     this.fieldAccessInfoCollection = fieldAccessInfoCollection;
@@ -288,7 +284,6 @@
         previous.failedMethodResolutionTargets,
         previous.failedFieldResolutionTargets,
         previous.bootstrapMethods,
-        previous.methodsTargetedByInvokeDynamic,
         previous.virtualMethodsTargetedByInvokeDirect,
         previous.liveMethods,
         previous.fieldAccessInfoCollection,
@@ -335,8 +330,6 @@
         pruneFields(previous.failedFieldResolutionTargets, prunedItems, executorService, futures),
         pruneMethods(previous.bootstrapMethods, prunedItems, executorService, futures),
         pruneMethods(
-            previous.methodsTargetedByInvokeDynamic, prunedItems, executorService, futures),
-        pruneMethods(
             previous.virtualMethodsTargetedByInvokeDirect, prunedItems, executorService, futures),
         pruneMethods(previous.liveMethods, prunedItems, executorService, futures),
         previous.fieldAccessInfoCollection,
@@ -541,7 +534,6 @@
         failedMethodResolutionTargets,
         failedFieldResolutionTargets,
         bootstrapMethods,
-        methodsTargetedByInvokeDynamic,
         virtualMethodsTargetedByInvokeDirect,
         liveMethods,
         fieldAccessInfoCollection,
@@ -621,7 +613,6 @@
     this.failedMethodResolutionTargets = previous.failedMethodResolutionTargets;
     this.failedFieldResolutionTargets = previous.failedFieldResolutionTargets;
     this.bootstrapMethods = previous.bootstrapMethods;
-    this.methodsTargetedByInvokeDynamic = previous.methodsTargetedByInvokeDynamic;
     this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
     this.liveMethods = previous.liveMethods;
     this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection;
@@ -745,14 +736,6 @@
     return isBootstrapMethod(method.getReference());
   }
 
-  public boolean isMethodTargetedByInvokeDynamic(DexMethod method) {
-    return methodsTargetedByInvokeDynamic.contains(method);
-  }
-
-  public boolean isMethodTargetedByInvokeDynamic(ProgramMethod method) {
-    return isMethodTargetedByInvokeDynamic(method.getReference());
-  }
-
   public Set<DexMethod> getVirtualMethodsTargetedByInvokeDirect() {
     return virtualMethodsTargetedByInvokeDirect;
   }
@@ -1232,7 +1215,6 @@
         lens.rewriteReferences(failedMethodResolutionTargets),
         lens.rewriteReferences(failedFieldResolutionTargets),
         lens.rewriteReferences(bootstrapMethods),
-        lens.rewriteReferences(methodsTargetedByInvokeDynamic),
         lens.rewriteReferences(virtualMethodsTargetedByInvokeDirect),
         lens.rewriteReferences(liveMethods),
         fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 8681f33..1125b65 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -57,7 +57,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.DirectMappedDexApplication.Builder;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
 import com.android.tools.r8.graph.FieldAccessInfoImpl;
@@ -356,10 +355,6 @@
    */
   private final Set<DexMethod> bootstrapMethods = Sets.newIdentityHashSet();
   /**
-   * Set of direct methods that are the immediate target of an invoke-dynamic.
-   */
-  private final Set<DexMethod> methodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
-  /**
    * Set of virtual methods that are the immediate target of an invoke-direct.
    */
   private final Set<DexMethod> virtualMethodsTargetedByInvokeDirect = Sets.newIdentityHashSet();
@@ -701,13 +696,17 @@
   private void addLiveNonProgramType(
       ClasspathOrLibraryClass clazz,
       // TODO(b/216576191): Remove when tracking live library members.
-      boolean visitMembers,
+      boolean markProgramSuperTypesAsLiveAndVisitMemberReferences,
       BiConsumer<DexType, ClasspathOrLibraryDefinition> missingClassConsumer) {
     WorkList<ClasspathOrLibraryClass> worklist =
         WorkList.newIdentityWorkList(clazz, liveNonProgramTypes);
     while (worklist.hasNext()) {
       ClasspathOrLibraryClass definition = worklist.next();
-      processNewLiveNonProgramType(definition, worklist, missingClassConsumer, visitMembers);
+      processNewLiveNonProgramType(
+          definition,
+          worklist,
+          missingClassConsumer,
+          markProgramSuperTypesAsLiveAndVisitMemberReferences);
     }
   }
 
@@ -715,13 +714,14 @@
       ClasspathOrLibraryClass clazz,
       WorkList<ClasspathOrLibraryClass> worklist,
       BiConsumer<DexType, ClasspathOrLibraryDefinition> missingClassConsumer,
-      boolean visitMembers) {
+      boolean markProgramSuperTypesAsLiveAndVisitMemberReferences) {
     ensureMethodsContinueToWidenAccess(clazz);
-    if (clazz.isLibraryClass()) {
-      // Only libraries must not derive program. Classpath classes can, assuming correct keep rules.
-      warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass());
-    }
-    if (visitMembers) {
+    if (markProgramSuperTypesAsLiveAndVisitMemberReferences) {
+      if (clazz.isLibraryClass()) {
+        // Only libraries must not derive program. Classpath classes can, assuming correct keep
+        // rules.
+        handleLibraryTypeInheritingFromProgramType(clazz.asLibraryClass());
+      }
       clazz.forEachClassField(
           field ->
               addNonProgramClassToWorklist(
@@ -792,7 +792,7 @@
     return asProgramClassOrNull(getClassOrNullFromReflectiveAccess(type, context));
   }
 
-  private void warnIfLibraryTypeInheritsFromProgramType(DexLibraryClass clazz) {
+  private void handleLibraryTypeInheritingFromProgramType(DexLibraryClass clazz) {
     if (clazz.superType != null) {
       ensureFromLibraryOrThrow(clazz.superType, clazz);
     }
@@ -1026,7 +1026,7 @@
         if (bootstrapArgument.isDexValueMethodHandle()) {
           DexMethodHandle method = bootstrapArgument.asDexValueMethodHandle().getValue();
           if (method.isMethodHandle()) {
-            methodsTargetedByInvokeDynamic.add(method.asMethod());
+            disableClosedWorldReasoning(method.asMethod(), context);
           }
         }
       }
@@ -1047,10 +1047,6 @@
     assert implHandle != null;
 
     DexMethod method = implHandle.asMethod();
-    if (!methodsTargetedByInvokeDynamic.add(method)) {
-      return;
-    }
-
     switch (implHandle.type) {
       case INVOKE_STATIC:
         traceInvokeStaticFromLambda(method, context);
@@ -1070,6 +1066,18 @@
       default:
         throw new Unreachable();
     }
+
+    disableClosedWorldReasoning(method, context);
+  }
+
+  private void disableClosedWorldReasoning(DexMethod reference, ProgramMethod context) {
+    SingleResolutionResult resolutionResult =
+        resolveMethod(reference, context, KeepReason.methodHandleReferencedIn(context));
+    if (resolutionResult != null && resolutionResult.getResolvedHolder().isProgramClass()) {
+      applyMinimumKeepInfoWhenLiveOrTargeted(
+          resolutionResult.getResolvedProgramMethod(),
+          KeepMethodInfo.newEmptyJoiner().disallowClosedWorldReasoning());
+    }
   }
 
   void traceCheckCast(DexType type, ProgramMethod currentMethod, boolean ignoreCompatRules) {
@@ -3420,9 +3428,8 @@
     }
   }
 
-  private void applyMinimumKeepInfoWhenLiveOrTargeted(
-      ProgramMethod method,
-      KeepMethodInfo.Joiner minimumKeepInfo) {
+  public void applyMinimumKeepInfoWhenLiveOrTargeted(
+      ProgramMethod method, KeepMethodInfo.Joiner minimumKeepInfo) {
     applyMinimumKeepInfoWhenLiveOrTargeted(method, minimumKeepInfo, EnqueuerEvent.unconditional());
   }
 
@@ -3801,10 +3808,14 @@
 
     // Add just referenced non-program types. We can't replace the program classes at this point as
     // they are needed in tree pruning.
-    Builder appBuilder = appInfo.app().asDirect().builder();
-    appBuilder.replaceLibraryClasses(libraryClasses);
-    appBuilder.replaceClasspathClasses(classpathClasses);
-    DirectMappedDexApplication app = appBuilder.build();
+    DirectMappedDexApplication app =
+        appInfo
+            .app()
+            .asDirect()
+            .builder()
+            .replaceLibraryClasses(libraryClasses)
+            .replaceClasspathClasses(classpathClasses)
+            .build();
 
     // Verify the references on the pruned application after type synthesis.
     assert verifyReferences(app);
@@ -3832,7 +3843,6 @@
             failedMethodResolutionTargets,
             failedFieldResolutionTargets,
             bootstrapMethods,
-            methodsTargetedByInvokeDynamic,
             virtualMethodsTargetedByInvokeDirect,
             toDescriptorSet(liveMethods.getItems()),
             // Filter out library fields and pinned fields, because these are read by default.
@@ -4347,7 +4357,7 @@
     }
 
     // Notify analyses.
-    analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context, workList));
+    analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context, this, workList));
   }
 
   private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index cd46a71..0173fb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -25,6 +25,7 @@
   }
 
   private final boolean allowClassInlining;
+  private final boolean allowClosedWorldReasoning;
   private final boolean allowConstantArgumentOptimization;
   private final boolean allowInlining;
   private final boolean allowMethodStaticizing;
@@ -37,6 +38,7 @@
   private KeepMethodInfo(Builder builder) {
     super(builder);
     this.allowClassInlining = builder.isClassInliningAllowed();
+    this.allowClosedWorldReasoning = builder.isClosedWorldReasoningAllowed();
     this.allowConstantArgumentOptimization = builder.isConstantArgumentOptimizationAllowed();
     this.allowInlining = builder.isInliningAllowed();
     this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
@@ -59,7 +61,8 @@
   }
 
   public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && !isCheckDiscardedEnabled(configuration);
   }
@@ -72,6 +75,14 @@
     return allowClassInlining;
   }
 
+  public boolean isClosedWorldReasoningAllowed(GlobalKeepInfoConfiguration configuration) {
+    return isOptimizationAllowed(configuration) && internalIsClosedWorldReasoningAllowed();
+  }
+
+  boolean internalIsClosedWorldReasoningAllowed() {
+    return allowClosedWorldReasoning;
+  }
+
   public boolean isConstantArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
     return isOptimizationAllowed(configuration) && internalIsConstantArgumentOptimizationAllowed();
   }
@@ -89,7 +100,8 @@
   }
 
   public boolean isMethodStaticizingAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && configuration.isMethodStaticizingEnabled()
         && internalIsMethodStaticizingAllowed();
@@ -100,7 +112,8 @@
   }
 
   public boolean isParameterReorderingAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsParameterReorderingAllowed();
   }
@@ -110,7 +123,8 @@
   }
 
   public boolean isParameterTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsParameterTypeStrengtheningAllowed();
   }
@@ -120,7 +134,8 @@
   }
 
   public boolean isReturnTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsReturnTypeStrengtheningAllowed();
   }
@@ -130,7 +145,8 @@
   }
 
   public boolean isUnusedArgumentOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsUnusedArgumentOptimizationAllowed();
   }
@@ -140,7 +156,8 @@
   }
 
   public boolean isUnusedReturnValueOptimizationAllowed(GlobalKeepInfoConfiguration configuration) {
-    return isOptimizationAllowed(configuration)
+    return isClosedWorldReasoningAllowed(configuration)
+        && isOptimizationAllowed(configuration)
         && isShrinkingAllowed(configuration)
         && internalIsUnusedReturnValueOptimizationAllowed();
   }
@@ -167,6 +184,7 @@
   public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
 
     private boolean allowClassInlining;
+    private boolean allowClosedWorldReasoning;
     private boolean allowConstantArgumentOptimization;
     private boolean allowInlining;
     private boolean allowMethodStaticizing;
@@ -183,6 +201,7 @@
     private Builder(KeepMethodInfo original) {
       super(original);
       allowClassInlining = original.internalIsClassInliningAllowed();
+      allowClosedWorldReasoning = original.internalIsClosedWorldReasoningAllowed();
       allowConstantArgumentOptimization = original.internalIsConstantArgumentOptimizationAllowed();
       allowInlining = original.internalIsInliningAllowed();
       allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
@@ -213,6 +232,25 @@
       return setAllowClassInlining(false);
     }
 
+    // Closed world reasoning.
+
+    public boolean isClosedWorldReasoningAllowed() {
+      return allowClosedWorldReasoning;
+    }
+
+    public Builder setAllowClosedWorldReasoning(boolean allowClosedWorldReasoning) {
+      this.allowClosedWorldReasoning = allowClosedWorldReasoning;
+      return self();
+    }
+
+    public Builder allowClosedWorldReasoning() {
+      return setAllowClosedWorldReasoning(true);
+    }
+
+    public Builder disallowClosedWorldReasoning() {
+      return setAllowClosedWorldReasoning(false);
+    }
+
     // Constant argument optimization.
 
     public boolean isConstantArgumentOptimizationAllowed() {
@@ -390,6 +428,7 @@
     boolean internalIsEqualTo(KeepMethodInfo other) {
       return super.internalIsEqualTo(other)
           && isClassInliningAllowed() == other.internalIsClassInliningAllowed()
+          && isClosedWorldReasoningAllowed() == other.internalIsClosedWorldReasoningAllowed()
           && isConstantArgumentOptimizationAllowed()
               == other.internalIsConstantArgumentOptimizationAllowed()
           && isInliningAllowed() == other.internalIsInliningAllowed()
@@ -413,6 +452,7 @@
     public Builder makeTop() {
       return super.makeTop()
           .disallowClassInlining()
+          .disallowClosedWorldReasoning()
           .disallowConstantArgumentOptimization()
           .disallowInlining()
           .disallowMethodStaticizing()
@@ -427,6 +467,7 @@
     public Builder makeBottom() {
       return super.makeBottom()
           .allowClassInlining()
+          .allowClosedWorldReasoning()
           .allowConstantArgumentOptimization()
           .allowInlining()
           .allowMethodStaticizing()
@@ -449,6 +490,11 @@
       return self();
     }
 
+    public Joiner disallowClosedWorldReasoning() {
+      builder.disallowClosedWorldReasoning();
+      return self();
+    }
+
     public Joiner disallowConstantArgumentOptimization() {
       builder.disallowConstantArgumentOptimization();
       return self();
@@ -500,6 +546,8 @@
       return super.merge(joiner)
           .applyIf(!joiner.builder.isClassInliningAllowed(), Joiner::disallowClassInlining)
           .applyIf(
+              !joiner.builder.isClosedWorldReasoningAllowed(), Joiner::disallowClosedWorldReasoning)
+          .applyIf(
               !joiner.builder.isConstantArgumentOptimizationAllowed(),
               Joiner::disallowConstantArgumentOptimization)
           .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index db5dcdc..f3391c8 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -89,7 +89,7 @@
         .replaceProgramClasses(getNewProgramClasses(application.classesWithDeterministicOrder()));
   }
 
-  private List<DexProgramClass> getNewProgramClasses(List<DexProgramClass> classes) {
+  private List<DexProgramClass> getNewProgramClasses(Collection<DexProgramClass> classes) {
     AppInfoWithLiveness appInfo = appView.appInfo();
     InternalOptions options = appView.options();
     List<DexProgramClass> newClasses = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 0ad5ad4..91f926c 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -2006,6 +2006,11 @@
     }
 
     @Override
+    public DexMethod getNextMethodSignature(DexMethod method) {
+      throw new Unreachable();
+    }
+
+    @Override
     public MethodLookupResult lookupMethod(
         DexMethod method, DexMethod context, Type type, GraphLens codeLens) {
       // First look up the method using the existing graph lens (for example, the type will have
diff --git a/src/main/java/com/android/tools/r8/utils/ClassMap.java b/src/main/java/com/android/tools/r8/utils/ClassMap.java
index 23d71ee..506a990 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassMap.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassMap.java
@@ -147,7 +147,7 @@
     return loadedClasses;
   }
 
-  public Map<DexType, T> getAllClassesInMap() {
+  public ImmutableMap<DexType, T> getAllClassesInMap() {
     if (classProvider.get() != null) {
       throw new Unreachable("Getting all classes from not fully loaded collection.");
     }
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 066ca9f..3fec28a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -54,11 +54,8 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.desugar.TypeRewriter;
 import com.android.tools.r8.ir.desugar.TypeRewriter.MachineDesugarPrefixRewritingMapper;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
 import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
@@ -352,6 +349,8 @@
   public boolean invalidDebugInfoStrict =
       System.getProperty("com.android.tools.r8.strictdebuginfo") != null;
 
+  public boolean ignoreJavaLibraryOverride = false;
+
   // When dexsplitting we ignore main dex classes missing in the application. These will be
   // fused together by play store when shipped for pre-L devices.
   public boolean ignoreMainDexMissingClasses = false;
@@ -563,6 +562,8 @@
   // public boolean lookupLibraryBeforeProgram =
   //     System.getProperty("com.android.tools.r8.lookupProgramBeforeLibrary") == null;
 
+  public boolean loadAllClassDefinitions = false;
+
   // Whether or not to check for valid multi-dex builds.
   //
   // For min-api levels that did not support native multi-dex the user should provide a main dex
@@ -889,38 +890,25 @@
   public StringConsumer configurationConsumer = null;
 
   public void setDesugaredLibrarySpecification(
-      LegacyDesugaredLibrarySpecification specification, AndroidApp app) {
-    if (specification.isEmptyConfiguration()) {
+      DesugaredLibrarySpecification specification, AndroidApp app) {
+    if (specification.isEmpty()) {
       return;
     }
     try {
-      HumanDesugaredLibrarySpecification human =
-          new LegacyToHumanSpecificationConverter()
-              .convert(specification, app.getLibraryResourceProviders(), this);
-      machineDesugaredLibrarySpecification =
-          new HumanToMachineSpecificationConverter()
-              .convert(
-                  human,
-                  specification.isLibraryCompilation() ? app.getProgramResourceProviders() : null,
-                  app.getLibraryResourceProviders(),
-                  this);
+      machineDesugaredLibrarySpecification = specification.toMachineSpecification(this, app);
     } catch (IOException e) {
       reporter.error(new ExceptionDiagnostic(e, Origin.unknown()));
     }
   }
 
   public void setDesugaredLibrarySpecificationForTesting(
-      LegacyDesugaredLibrarySpecification specification, Path desugaredJDKLib, Path library)
+      DesugaredLibrarySpecification specification, Path desugaredJDKLib, Path library)
       throws IOException {
-    HumanDesugaredLibrarySpecification human =
-        new LegacyToHumanSpecificationConverter().convert(specification, library, this);
+    if (specification.isEmpty()) {
+      return;
+    }
     machineDesugaredLibrarySpecification =
-        new HumanToMachineSpecificationConverter()
-            .convert(
-                human,
-                specification.isLibraryCompilation() ? desugaredJDKLib : null,
-                library,
-                this);
+        specification.toMachineSpecification(this, library, desugaredJDKLib);
   }
 
   // Contains flags describing library desugaring.
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index a29af0b..2f55680 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -9,6 +9,7 @@
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
 import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
 import java.util.function.Function;
 import java.util.function.IntFunction;
@@ -63,14 +64,28 @@
       Function<K1, K2> keyMapping,
       Function<V1, V2> valueMapping,
       TriFunction<K2, V2, V2, V2> valueMerger) {
+    return transform(
+        map,
+        factory,
+        (key, value) -> keyMapping.apply(key),
+        (key, value) -> valueMapping.apply(value),
+        valueMerger);
+  }
+
+  public static <K1, V1, K2, V2> Map<K2, V2> transform(
+      Map<K1, V1> map,
+      IntFunction<Map<K2, V2>> factory,
+      BiFunction<K1, V1, K2> keyMapping,
+      BiFunction<K1, V1, V2> valueMapping,
+      TriFunction<K2, V2, V2, V2> valueMerger) {
     Map<K2, V2> result = factory.apply(map.size());
     map.forEach(
         (key, value) -> {
-          K2 newKey = keyMapping.apply(key);
+          K2 newKey = keyMapping.apply(key, value);
           if (newKey == null) {
             return;
           }
-          V2 newValue = valueMapping.apply(value);
+          V2 newValue = valueMapping.apply(key, value);
           V2 existingValue = result.put(newKey, newValue);
           if (existingValue != null) {
             result.put(newKey, valueMerger.apply(newKey, existingValue, newValue));
diff --git a/src/test/examplesJava9/backport/IntegerBackportJava9Main.java b/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
index 8188fe1..acf18d2 100644
--- a/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
+++ b/src/test/examplesJava9/backport/IntegerBackportJava9Main.java
@@ -4,8 +4,6 @@
 
 package backport;
 
-import java.math.BigInteger;
-
 public final class IntegerBackportJava9Main {
   private static final int[] interestingValues = {
     Integer.MIN_VALUE,
@@ -23,6 +21,7 @@
 
   public static void main(String[] args) {
     testParseIntegerSubsequenceWithRadix();
+    testParseUnsignedIntegerSubsequenceWithRadix();
   }
 
   private static void testParseIntegerSubsequenceWithRadix() {
@@ -71,22 +70,87 @@
     } catch (NumberFormatException expected) {
     }
 
-    BigInteger overflow = new BigInteger("18446744073709551616");
+    long overflow = 73709551616L;
     for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) {
       for (String prefix : new String[] {"", "x", "xxx"}) {
         for (String postfix : new String[] {"", "x", "xxx"}) {
-          String overflowString = prefix + overflow.toString(radix) + postfix;
+          String overflowString = prefix + Long.toString(overflow, radix) + postfix;
           int start = prefix.length();
           int end = overflowString.length() - postfix.length();
           try {
-            throw new AssertionError(Long.parseLong(overflowString, start, end, radix));
+            throw new AssertionError(Integer.parseInt(overflowString, start, end, radix));
           } catch (NumberFormatException expected) {
           }
-          String underflowString = prefix + '-' + overflow.toString(radix) + postfix;
+          String underflowString = prefix + '-' + Long.toString(overflow, radix) + postfix;
           start = prefix.length();
           end = underflowString.length() - postfix.length();
           try {
-            throw new AssertionError(Long.parseLong(underflowString, start, end, radix));
+            throw new AssertionError(Integer.parseInt(underflowString, start, end, radix));
+          } catch (NumberFormatException expected) {
+          }
+        }
+      }
+    }
+  }
+
+  private static void testParseUnsignedIntegerSubsequenceWithRadix() {
+    for (int value : interestingValues) {
+      for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) {
+        for (String prefix : new String[] {"", "x", "xxx"}) {
+          for (String postfix : new String[] {"", "x", "xxx"}) {
+            String unsignedIntergerString = Long.toString(Integer.toUnsignedLong(value), radix);
+            String valueString = prefix + unsignedIntergerString + postfix;
+            int start = prefix.length();
+            int end = valueString.length() - postfix.length();
+            assertEquals(
+                valueString, value, Integer.parseUnsignedInt(valueString, start, end, radix));
+            if (value > 0) {
+              valueString = prefix + '+' + unsignedIntergerString + postfix;
+              end++;
+              assertEquals(
+                  valueString, value, Integer.parseUnsignedInt(valueString, start, end, radix));
+            }
+          }
+        }
+      }
+    }
+
+    try {
+      throw new AssertionError(Integer.parseUnsignedInt("0", 0, 1, Character.MIN_RADIX - 1));
+    } catch (IllegalArgumentException expected) {
+    }
+    try {
+      throw new AssertionError(Integer.parseUnsignedInt("0", 0, 1, Character.MAX_RADIX + 1));
+    } catch (IllegalArgumentException expected) {
+    }
+
+    try {
+      throw new AssertionError(Integer.parseUnsignedInt("", 0, 0, 16));
+    } catch (NumberFormatException expected) {
+    }
+    try {
+      throw new AssertionError(Integer.parseUnsignedInt("-", 0, 1, 16));
+    } catch (NumberFormatException expected) {
+    }
+    try {
+      throw new AssertionError(Integer.parseUnsignedInt("+", 0, 1, 16));
+    } catch (NumberFormatException expected) {
+    }
+
+    try {
+      throw new AssertionError(Integer.parseUnsignedInt("+a", 0, 2, 10));
+    } catch (NumberFormatException expected) {
+    }
+
+    long overflow = 73709551616L;
+    for (int radix = Character.MIN_RADIX; radix <= Character.MAX_RADIX; radix++) {
+      for (String prefix : new String[] {"", "x", "xxx"}) {
+        for (String postfix : new String[] {"", "x", "xxx"}) {
+          String overflowString = prefix + Long.toString(overflow, radix) + postfix;
+          int start = prefix.length();
+          int end = overflowString.length() - postfix.length();
+          try {
+            throw new AssertionError(Integer.parseUnsignedInt(overflowString, start, end, radix));
           } catch (NumberFormatException expected) {
           }
         }
diff --git a/src/test/java/com/android/tools/r8/MarkersTest.java b/src/test/java/com/android/tools/r8/MarkersTest.java
index d883360..a38cf08 100644
--- a/src/test/java/com/android/tools/r8/MarkersTest.java
+++ b/src/test/java/com/android/tools/r8/MarkersTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.Origin;
@@ -27,6 +28,7 @@
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
+import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import java.nio.file.Path;
 import java.util.Collection;
@@ -36,7 +38,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class MarkersTest extends TestBase {
+public class MarkersTest extends DesugaredLibraryTestBase {
 
   @Parameterized.Parameters(
       name = "{0}, compilationMode {1}, shrinkDesugaredLibrary {2}, noCfMarkerForDesugaredCode {3}")
@@ -84,18 +86,20 @@
     }
     L8.run(builder.build());
     Collection<Marker> markers = ExtractMarker.extractMarkerFromDexFile(output);
-    String version =
+    JsonObject jsonObject =
         new JsonParser()
             .parse(FileUtils.readTextFile(ToolHelper.getDesugarLibJsonForTesting(), Charsets.UTF_8))
-            .getAsJsonObject()
-            .get("version")
-            .getAsString();
+            .getAsJsonObject();
+    String identifier =
+        jsonObject.has("version")
+            ? "com.tools.android:desugar_jdk_libs:" + jsonObject.get("version").getAsString()
+            : jsonObject.get("identifier").getAsString();
 
     Matcher<Marker> l8Matcher =
         allOf(
             markerTool(Tool.L8),
             markerCompilationMode(compilationMode),
-            markerDesugaredLibraryIdentifier("com.tools.android:desugar_jdk_libs:" + version),
+            markerDesugaredLibraryIdentifier(identifier),
             markerHasChecksums(false));
     Matcher<Marker> r8Matcher =
         allOf(
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 028b4b7..e6cd425 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -297,17 +297,22 @@
         .put(
             DexVm.Version.V9_0_0,
             ImmutableList.of(
-                // TODO(120402963): Triage.
+                // TODO(b/120402963): Triage.
                 "invokecustom", "invokecustom2"))
         .put(
             DexVm.Version.V10_0_0,
             ImmutableList.of(
-                // TODO(120402963): Triage.
+                // TODO(b/120402963): Triage.
                 "invokecustom", "invokecustom2"))
         .put(
             DexVm.Version.V12_0_0,
             ImmutableList.of(
-                // TODO(120402963): Triage.
+                // TODO(b/120402963): Triage.
+                "invokecustom", "invokecustom2"))
+        .put(
+            Version.V13_MASTER,
+            ImmutableList.of(
+                // TODO(b/120402963): Triage.
                 "invokecustom", "invokecustom2"))
         .put(DexVm.Version.DEFAULT, ImmutableList.of());
     failsOn = builder.build();
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
index 2cb5556..d0db2c9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/ApiLevelBackportsTest.java
@@ -6,7 +6,6 @@
 
 import static org.hamcrest.core.StringContains.containsString;
 
-import com.android.tools.r8.D8TestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -50,24 +49,31 @@
         .compile()
         .assertNoMessages()
         .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
-        .assertSuccessWithOutputLines("4");
+        .applyIf(
+            runtimeHasParseIntWithFourArgs(),
+            r -> r.assertSuccessWithOutputLines("4"),
+            r ->
+                r.assertFailureWithErrorThatMatches(
+                    containsString(
+                        "java.lang.NoSuchMethodError: No static method"
+                            + " parseInt(Ljava/lang/CharSequence;III)I")));
   }
 
   @Test
   public void backportOfPresentMethodOnLatest() throws Exception {
-    D8TestRunResult result =
-        testForD8()
-            .addProgramClassFileData(transformTestListOf())
-            .setMinApi(AndroidApiLevel.LATEST)
-            .compile()
-            .assertNoMessages()
-            .run(parameters.getRuntime(), TestListOf.class);
-    if (runtimeHasListOf()) {
-      result.assertSuccessWithOutputLines("0");
-    } else {
-      result.assertFailureWithErrorThatMatches(
-          containsString("java.lang.NoSuchMethodError: No static method of()Ljava/util/List;"));
-    }
+    testForD8()
+        .addProgramClassFileData(transformTestListOf())
+        .setMinApi(AndroidApiLevel.LATEST)
+        .compile()
+        .assertNoMessages()
+        .run(parameters.getRuntime(), TestListOf.class)
+        .applyIf(
+            runtimeHasListOf(),
+            r -> r.assertSuccessWithOutputLines("0"),
+            r ->
+                r.assertFailureWithErrorThatMatches(
+                    containsString(
+                        "java.lang.NoSuchMethodError: No static method of()Ljava/util/List;")));
   }
 
   @Test
@@ -79,7 +85,14 @@
         .assertOnlyWarnings()
         .assertWarningMessageThatMatches(containsString("is not supported by this compiler"))
         .run(parameters.getRuntime(), TestMathMultiplyExactLongInt.class)
-        .assertSuccessWithOutputLines("4");
+        .applyIf(
+            runtimeHasParseIntWithFourArgs(),
+            r -> r.assertSuccessWithOutputLines("4"),
+            r ->
+                r.assertFailureWithErrorThatMatches(
+                    containsString(
+                        "java.lang.NoSuchMethodError: No static method"
+                            + " parseInt(Ljava/lang/CharSequence;III)I")));
   }
 
   @Test
@@ -109,6 +122,14 @@
         .isGreaterThanOrEqualTo(AndroidApiLevel.R);
   }
 
+  boolean runtimeHasParseIntWithFourArgs() {
+    return parameters
+        .getRuntime()
+        .asDex()
+        .getMinApiLevel()
+        .isGreaterThanOrEqualTo(AndroidApiLevel.T);
+  }
+
   byte[] transformTestListOf() throws Exception {
     return transformer(TestListOf.class)
         .transformMethodInsnInMethod(
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
index a2e1120..f8c5a53 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/IntegerBackportJava9Test.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import org.junit.runner.RunWith;
@@ -30,8 +31,11 @@
       Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
 
   public IntegerBackportJava9Test(TestParameters parameters) {
-    super(parameters, Short.class, TEST_JAR, "backport.IntegerBackportJava9Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to IntegerBackportTest.
+    super(parameters, Integer.class, TEST_JAR, "backport.IntegerBackportJava9Main");
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to IntegerBackportTest (out of examplesJava9).
+
+    registerTarget(AndroidApiLevel.O, 1);
+    registerTarget(AndroidApiLevel.T, 20);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
index 4bd90c8..487f80f 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/LongBackportJava9Test.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import org.junit.runner.RunWith;
@@ -30,8 +31,12 @@
       Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR).resolve("backport" + JAR_EXTENSION);
 
   public LongBackportJava9Test(TestParameters parameters) {
-    super(parameters, Short.class, TEST_JAR, "backport.LongBackportJava9Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to LongBackportTest.
+    super(parameters, Long.class, TEST_JAR, "backport.LongBackportJava9Main");
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to LongBackportTest (out of examplesJava9).
+
+    ignoreInvokes("toString");
+
+    registerTarget(AndroidApiLevel.T, 17);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
index b7f4b6d..6290238 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/StringBackportJava11Test.java
@@ -4,17 +4,18 @@
 
 package com.android.tools.r8.desugar.backports;
 
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-
 @RunWith(Parameterized.class)
 public final class StringBackportJava11Test extends AbstractBackportTest {
   @Parameters(name = "{0}")
@@ -31,7 +32,9 @@
 
   public StringBackportJava11Test(TestParameters parameters) {
     super(parameters, String.class, TEST_JAR, "backport.StringBackportJava11Main");
-    // Note: None of the methods in this test exist in the latest android.jar. If/when they ship in
-    // an actual API level, migrate these tests to CharacterBackportTest.
+    // Note: The methods in this test exist in android.jar from Android T. When R8 builds targeting
+    // Java 11 move these tests to StringBackportTest (out of examplesJava11).
+
+    registerTarget(AndroidApiLevel.T, 49);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index b5e5c03..6d3a0d3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -10,8 +10,8 @@
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -60,16 +60,16 @@
             : "Caught j$.io.UncheckedIOException");
   }
 
-  LegacyDesugaredLibrarySpecification configurationAlternative3(
+  DesugaredLibrarySpecification configurationAlternative3(
       InternalOptions options, boolean libraryCompilation, TestParameters parameters) {
     // Parse the current configuration and amend the configuration for BufferedReader.lines. The
     // configuration is the same for both program and library.
-    return new LegacyDesugaredLibrarySpecificationParser(
-            options.dexItemFactory(),
-            options.reporter,
-            libraryCompilation,
-            parameters.getApiLevel().getLevel())
-        .parse(StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingAlternative3()));
+    return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+        StringResource.fromFile(ToolHelper.getDesugarLibJsonForTestingAlternative3()),
+        options.dexItemFactory(),
+        options.reporter,
+        libraryCompilation,
+        parameters.getApiLevel().getLevel());
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
index e27dda9..4d87dbf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCHMOnlyContentTest.java
@@ -8,8 +8,8 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.nio.file.Path;
@@ -67,13 +67,13 @@
     assert inspector.clazz("j$.util.concurrent.ConcurrentHashMap").isPresent();
   }
 
-  LegacyDesugaredLibrarySpecification chmOnlyConfiguration(
+  DesugaredLibrarySpecification chmOnlyConfiguration(
       InternalOptions options, boolean libraryCompilation, TestParameters parameters) {
-    return new LegacyDesugaredLibrarySpecificationParser(
-            options.dexItemFactory(),
-            options.reporter,
-            libraryCompilation,
-            parameters.getApiLevel().getLevel())
-        .parse(StringResource.fromFile(ToolHelper.getCHMOnlyDesugarLibJsonForTesting()));
+    return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+        StringResource.fromFile(ToolHelper.getCHMOnlyDesugarLibJsonForTesting()),
+        options.dexItemFactory(),
+        options.reporter,
+        libraryCompilation,
+        parameters.getApiLevel().getLevel());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index f4786f7..2684b3b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -5,16 +5,15 @@
 package com.android.tools.r8.desugar.desugaredlibrary;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.fail;
 
+import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.L8Command;
-import com.android.tools.r8.L8TestBuilder;
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.StringResource;
 import com.android.tools.r8.TestDiagnosticMessages;
@@ -29,7 +28,6 @@
 import com.android.tools.r8.utils.codeinspector.InstructionSubject;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Collections;
 import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,22 +48,22 @@
   }
 
   @Test
-  public void testInvalidLibrary() throws Exception {
+  public void testInvalidLibrary() {
     Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
-    L8TestBuilder l8TestBuilder =
-        testForL8(parameters.getApiLevel())
-            .addProgramFiles(Collections.singleton(ToolHelper.getDesugarJDKLibs()))
+    D8TestBuilder testBuilder =
+        testForD8()
+            .setMinApi(parameters.getApiLevel())
+            .addProgramClasses(GuineaPig.class)
             .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.L))
-            .setDesugarJDKLibsConfiguration(ToolHelper.DESUGAR_LIB_CONVERSIONS);
+            .enableCoreLibraryDesugaring(
+                LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel()));
     try {
-      l8TestBuilder.compile();
-      fail();
-    } catch (AssertionError ae) {
-      // Expected since the library is invalid.
+      testBuilder.compile();
+    } catch (Throwable t) {
+      // Expected since we are compiling with an invalid set-up.
     }
-    TestDiagnosticMessages diagnosticMessages = l8TestBuilder.getDiagnosticMessages();
+    TestDiagnosticMessages diagnosticMessages = testBuilder.getState().getDiagnosticsMessages();
     diagnosticMessages.assertOnlyWarnings();
-    assertEquals(diagnosticMessages.getWarnings().size(), 1);
     assertTrue(
         diagnosticMessages
             .getWarnings()
@@ -121,21 +119,7 @@
     ToolHelper.runL8(l8Builder.build(), options -> {});
     CodeInspector codeInspector = new CodeInspector(desugaredLib);
     assertCorrect(codeInspector);
-    assertOneWarning(diagnosticsHandler);
-  }
-
-  private void assertOneWarning(TestDiagnosticMessagesImpl diagnosticsHandler) {
-    assertEquals(
-        (isJDK11DesugaredLibrary() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O))
-            ? 2
-            : 1,
-        diagnosticsHandler.getWarnings().size());
-    String msg = diagnosticsHandler.getWarnings().get(0).getDiagnosticMessage();
-    assertTrue(
-        msg.contains(
-            "The following library types, prefixed by java., are present both as library and non"
-                + " library classes"));
-    assertTrue(diagnosticsHandler.getErrors().isEmpty());
+    diagnosticsHandler.assertNoMessages();
   }
 
   private void assertCorrect(CodeInspector inspector) {
@@ -166,4 +150,8 @@
     }
   }
 
+  static class GuineaPig {
+
+    public static void main(String[] args) {}
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
index 1409ec7..d80d01d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
@@ -65,14 +65,18 @@
           .enableCoreLibraryDesugaring(LibraryDesugaringTestConfiguration.forApiLevel(apiLevel))
           .compileWithExpectedDiagnostics(
               diagnostics -> {
-                diagnostics.assertNoInfos();
-                diagnostics.assertAllWarningsMatch(
-                    diagnosticMessage(
-                        containsString(
-                            "The compilation is slowed down due to a mix of class file and dex"
-                                + " file inputs in the context of desugared library.")));
-                diagnostics.assertErrorsMatch(
-                    diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                if (requiresAnyCoreLibDesugaring(apiLevel)) {
+                  diagnostics.assertNoInfos();
+                  diagnostics.assertAllWarningsMatch(
+                      diagnosticMessage(
+                          containsString(
+                              "The compilation is slowed down due to a mix of class file and dex"
+                                  + " file inputs in the context of desugared library.")));
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
               });
 
     } catch (CompilationFailedException e) {
@@ -146,9 +150,13 @@
           .setMinApi(apiLevel)
           .compileWithExpectedDiagnostics(
               diagnostics -> {
-                diagnostics.assertOnlyErrors();
-                diagnostics.assertErrorsMatch(
-                    diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                if (requiresAnyCoreLibDesugaring(apiLevel)) {
+                  diagnostics.assertOnlyErrors();
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
               });
     } catch (CompilationFailedException e) {
     }
@@ -181,9 +189,13 @@
           .setMinApi(apiLevel)
           .compileWithExpectedDiagnostics(
               diagnostics -> {
-                diagnostics.assertOnlyErrors();
-                diagnostics.assertErrorsMatch(
-                    diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                if (requiresAnyCoreLibDesugaring(apiLevel)) {
+                  diagnostics.assertOnlyErrors();
+                  diagnostics.assertErrorsMatch(
+                      diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
+                } else {
+                  diagnostics.assertNoMessages();
+                }
               });
     } catch (CompilationFailedException e) {
     }
@@ -211,7 +223,10 @@
                                 + "\"version\":\"1.0.9\","
                                 + "\"synthesized_library_classes_package_prefix\":\"my_prefix\","
                                 + "\"required_compilation_api_level\":\"30\","
-                                + "\"common_flags\":[],"
+                                + "\"common_flags\":[{"
+                                + "      \"api_level_below_or_equal\": 9999,"
+                                + "      \"rewrite_prefix\": {\"j$.time.\": \"java.time.\"}"
+                                + "    }],"
                                 + "\"library_flags\":[],"
                                 + "\"program_flags\":[]"
                                 + "}",
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index eb25adf..56f687c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -24,8 +24,8 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.tracereferences.TraceReferences;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
@@ -68,7 +68,7 @@
   }
 
   public void setDesugaredLibrarySpecificationForTesting(
-      InternalOptions options, LegacyDesugaredLibrarySpecification specification) {
+      InternalOptions options, DesugaredLibrarySpecification specification) {
     try {
       options.setDesugaredLibrarySpecificationForTesting(
           specification,
@@ -117,10 +117,14 @@
   }
 
   protected boolean requiresAnyCoreLibDesugaring(TestParameters parameters) {
-    return parameters.getApiLevel().getLevel()
+    return requiresAnyCoreLibDesugaring(parameters.getApiLevel());
+  }
+
+  protected boolean requiresAnyCoreLibDesugaring(AndroidApiLevel apiLevel) {
+    return apiLevel.getLevel()
         <= (isJDK11DesugaredLibrary()
             ? AndroidApiLevel.LATEST.getLevel()
-            : AndroidApiLevel.N.getLevel());
+            : AndroidApiLevel.N_MR1.getLevel());
   }
 
   protected L8TestBuilder testForL8(AndroidApiLevel apiLevel) {
@@ -245,19 +249,18 @@
     return desugaredLib;
   }
 
-  protected LegacyDesugaredLibrarySpecification configurationWithSupportAllCallbacksFromLibrary(
+  protected DesugaredLibrarySpecification configurationWithSupportAllCallbacksFromLibrary(
       InternalOptions options,
       boolean libraryCompilation,
       TestParameters parameters,
       boolean supportAllCallbacksFromLibrary) {
-    return new LegacyDesugaredLibrarySpecificationParser(
-            options.dexItemFactory(),
-            options.reporter,
-            libraryCompilation,
-            parameters.getApiLevel().getLevel())
-        .parse(
-            StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()),
-            builder -> builder.setSupportAllCallbacksFromLibrary(supportAllCallbacksFromLibrary));
+    return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecificationforTesting(
+        StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()),
+        options.dexItemFactory(),
+        options.reporter,
+        libraryCompilation,
+        parameters.getApiLevel().getLevel(),
+        builder -> builder.setSupportAllCallbacksFromLibrary(supportAllCallbacksFromLibrary));
   }
 
   private Map<AndroidApiLevel, Path> desugaredLibraryClassFileCache = new HashMap<>();
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
index 47b01f1..0c61a06 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryWarningTest.java
@@ -4,9 +4,6 @@
 
 package com.android.tools.r8.desugar.desugaredlibrary;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.L8Command;
 import com.android.tools.r8.OutputMode;
@@ -15,7 +12,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import java.nio.file.Path;
 import java.util.Arrays;
@@ -69,19 +65,6 @@
           Arrays.asList(FUNCTION_KEEP.split(System.lineSeparator())), Origin.unknown());
     }
     ToolHelper.runL8(l8Builder.build(), options -> {});
-    assertEquals(
-        (isJDK11DesugaredLibrary() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O))
-            ? 2
-            : 1,
-        diagnosticsHandler.getWarnings().size());
-    diagnosticsHandler.assertNoErrors();
-    assertTrue(
-        diagnosticsHandler
-            .getWarnings()
-            .get(0)
-            .getDiagnosticMessage()
-            .contains(
-                "The following library types, prefixed by java., are present both as library and"
-                    + " non library classes:"));
+    diagnosticsHandler.assertNoMessages();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 21dc2f9..86d87d5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -10,20 +10,22 @@
 
 import com.android.tools.r8.GenerateLintFiles;
 import com.android.tools.r8.StringResource;
-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.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 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.references.TypeReference;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -46,7 +48,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class ExtractWrapperTypesTest extends TestBase {
+public class ExtractWrapperTypesTest extends DesugaredLibraryTestBase {
 
   // Filter on types that do not need to be considered for wrapping.
   private static boolean doesNotNeedWrapper(String type, Set<String> customConversions) {
@@ -128,11 +130,22 @@
     CodeInspector desugaredApiJar = getDesugaredApiJar();
     Set<ClassReference> preDesugarTypes = getPreDesugarTypes();
 
-    LegacyDesugaredLibrarySpecification spec = getDesugaredLibraryConfiguration();
+    DexItemFactory factory = new DexItemFactory();
+    DesugaredLibrarySpecification spec =
+        DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+            StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()),
+            factory,
+            null,
+            false,
+            minApi.getLevel());
+    MachineDesugaredLibrarySpecification specification =
+        spec.toMachineSpecification(new InternalOptions(factory, new Reporter()), getLibraryFile());
     Set<String> wrappersInSpec =
-        spec.getWrapperConversions().stream().map(DexType::toString).collect(Collectors.toSet());
+        specification.getWrappers().keySet().stream()
+            .map(DexType::toString)
+            .collect(Collectors.toSet());
     Set<String> customConversionsInSpec =
-        spec.getCustomConversions().keySet().stream()
+        specification.getCustomConversions().keySet().stream()
             .map(DexType::toString)
             .collect(Collectors.toSet());
     assertEquals(
@@ -191,13 +204,6 @@
     return missingWrappers;
   }
 
-  private LegacyDesugaredLibrarySpecification getDesugaredLibraryConfiguration() {
-    LegacyDesugaredLibrarySpecificationParser parser =
-        new LegacyDesugaredLibrarySpecificationParser(
-            new DexItemFactory(), null, true, minApi.getLevel());
-    return parser.parse(StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()));
-  }
-
   private Map<ClassReference, Set<MethodReference>> getDirectlyReferencedWrapperTypes(
       CodeInspector desugaredApiJar,
       Set<ClassReference> preDesugarTypes,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LegacyDesugaredLibraryConfigurationParsingTest.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LegacyDesugaredLibraryConfigurationParsingTest.java
index efc5ce8..83316eb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LegacyDesugaredLibraryConfigurationParsingTest.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar.desugaredlibrary;
@@ -12,7 +12,6 @@
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.StringResource;
-import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestDiagnosticMessages;
 import com.android.tools.r8.TestDiagnosticMessagesImpl;
 import com.android.tools.r8.TestParameters;
@@ -38,19 +37,20 @@
 import java.util.Map;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class DesugaredLibraryConfigurationParsingTest extends TestBase {
+public class LegacyDesugaredLibraryConfigurationParsingTest extends DesugaredLibraryTestBase {
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withNoneRuntime().build();
   }
 
-  public DesugaredLibraryConfigurationParsingTest(TestParameters parameters) {
+  public LegacyDesugaredLibraryConfigurationParsingTest(TestParameters parameters) {
     parameters.assertNoneRuntime();
   }
 
@@ -87,6 +87,7 @@
   }
 
   private LegacyDesugaredLibrarySpecificationParser parser(DiagnosticsHandler handler) {
+    Assume.assumeFalse(isJDK11DesugaredLibrary());
     return new LegacyDesugaredLibrarySpecificationParser(
         factory, new Reporter(handler), libraryCompilation, minApi.getLevel());
   }
@@ -179,7 +180,7 @@
   @Test
   public void testUnsupportedAbove() {
     LinkedHashMap<String, Object> data = template();
-    data.put("configuration_format_version", 100000);
+    data.put("configuration_format_version", 99);
     runFailing(
         toJson(data),
         diagnostics ->
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index d296ec9..cd54e5d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -14,8 +14,8 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -125,10 +125,13 @@
           directory.toString()
         });
     InternalOptions options = new InternalOptions(new DexItemFactory(), new Reporter());
-    LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
-        new LegacyDesugaredLibrarySpecificationParser(
-                options.itemFactory, options.reporter, false, AndroidApiLevel.B.getLevel())
-            .parse(StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()));
+    DesugaredLibrarySpecification desugaredLibrarySpecification =
+        DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+            StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()),
+            options.itemFactory,
+            options.reporter,
+            false,
+            AndroidApiLevel.B.getLevel());
 
     for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
       if (apiLevel.isGreaterThan(AndroidApiLevel.Sv2)) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index ed772cb..c21b7ea 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -15,8 +15,8 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -84,18 +84,17 @@
             Ordered.max(parameters.getApiLevel(), getRequiredCompilationAPILevel()));
   }
 
-  LegacyDesugaredLibrarySpecification desugaredLibrarySpecification(
+  DesugaredLibrarySpecification desugaredLibrarySpecification(
       InternalOptions options, boolean libraryCompilation, TestParameters parameters) {
-    return new LegacyDesugaredLibrarySpecificationParser(
-            options.dexItemFactory(),
-            options.reporter,
-            libraryCompilation,
-            parameters.getApiLevel().getLevel())
-        .parse(
-            StringResource.fromFile(
-                libraryDesugarJavaUtilObjects
-                    ? ToolHelper.getDesugarLibJsonForTestingAlternative3()
-                    : ToolHelper.getDesugarLibJsonForTesting()));
+    return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+        StringResource.fromFile(
+            libraryDesugarJavaUtilObjects
+                ? ToolHelper.getDesugarLibJsonForTestingAlternative3()
+                : ToolHelper.getDesugarLibJsonForTesting()),
+        options.dexItemFactory(),
+        options.reporter,
+        libraryCompilation,
+        parameters.getApiLevel().getLevel());
   }
 
   private void configurationForProgramCompilation(InternalOptions options) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index 4d861a0..125b4ec 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -8,10 +8,10 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.StringResource;
-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.desugar.desugaredlibrary.DesugaredLibraryTestBase;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification;
@@ -31,9 +31,7 @@
 import org.junit.runners.Parameterized;
 
 @RunWith(Parameterized.class)
-public class ConvertExportReadTest extends TestBase {
-
-  private final TestParameters parameters;
+public class ConvertExportReadTest extends DesugaredLibraryTestBase {
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
@@ -41,7 +39,7 @@
   }
 
   public ConvertExportReadTest(TestParameters parameters) {
-    this.parameters = parameters;
+    assert parameters.isNoneRuntime();
   }
 
   @Test
@@ -98,25 +96,27 @@
       HumanRewritingFlags humanRewritingFlags1, HumanRewritingFlags humanRewritingFlags2) {
     assertEquals(humanRewritingFlags1.getRewritePrefix(), humanRewritingFlags2.getRewritePrefix());
     assertEquals(
-        humanRewritingFlags1.getBackportCoreLibraryMember(),
-        humanRewritingFlags2.getBackportCoreLibraryMember());
+        humanRewritingFlags1.getRewriteDerivedPrefix(),
+        humanRewritingFlags2.getRewriteDerivedPrefix());
+
+    assertEquals(
+        humanRewritingFlags1.getLegacyBackport(), humanRewritingFlags2.getLegacyBackport());
     assertEquals(
         humanRewritingFlags1.getCustomConversions(), humanRewritingFlags2.getCustomConversions());
     assertEquals(
-        humanRewritingFlags1.getEmulateLibraryInterface(),
-        humanRewritingFlags2.getEmulateLibraryInterface());
+        humanRewritingFlags1.getEmulatedInterfaces(), humanRewritingFlags2.getEmulatedInterfaces());
     assertEquals(
-        humanRewritingFlags1.getRetargetCoreLibMember(),
-        humanRewritingFlags2.getRetargetCoreLibMember());
+        humanRewritingFlags1.getRetargetMethod(), humanRewritingFlags2.getRetargetMethod());
 
-    assertEquals(
-        humanRewritingFlags1.getDontRetargetLibMember(),
-        humanRewritingFlags2.getDontRetargetLibMember());
+    assertEquals(humanRewritingFlags1.getDontRetarget(), humanRewritingFlags2.getDontRetarget());
     assertEquals(
         humanRewritingFlags1.getDontRewriteInvocation(),
         humanRewritingFlags2.getDontRewriteInvocation());
     assertEquals(
         humanRewritingFlags1.getWrapperConversions(), humanRewritingFlags2.getWrapperConversions());
+
+    assertEquals(
+        humanRewritingFlags1.getAmendLibraryMethod(), humanRewritingFlags2.getAmendLibraryMethod());
   }
 
   private void assertTopLevelFlagsEquals(
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index cbe8874..5b700d5 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -138,7 +138,7 @@
   }
 
   @Test(expected = CompilationFailedException.class)
-  @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.V12_0_0) // No desugaring
+  @IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.V13_MASTER) // No desugaring
   public void testInvokeDefault1() throws Exception {
     ensureSameOutput(
         TestMainDefault1.class.getCanonicalName(),
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
index da89df6..4b04321 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
@@ -33,6 +33,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiPredicate;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -121,7 +122,7 @@
                     MapUtils.transform(
                         substitutions,
                         HashMap::new,
-                        s -> s,
+                        Function.identity(),
                         ClassTypeSignature::new,
                         (key, val1, val2) -> {
                           throw new Unreachable("No keys should be merged");
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java
index bc84108..b7ddc31 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/IntegerMethods.java
@@ -73,4 +73,9 @@
     }
     return Integer.parseInt(s.subSequence(beginIndex, endIndex).toString(), radix);
   }
+
+  public static int parseUnsignedIntSubsequenceWithRadix(
+      CharSequence s, int beginIndex, int endIndex, int radix) throws NumberFormatException {
+    return Integer.parseUnsignedInt(s.subSequence(beginIndex, endIndex).toString(), radix);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java
index 5cc9de6..aa3a51d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInnerClassTest.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.TestParameters;
@@ -41,6 +42,20 @@
 
   private final TestParameters parameters;
 
+  private String getExpected() {
+    return replaceInitNameInExpectedBasedOnKotlinVersion(EXPECTED);
+  }
+
+  private String getExpectedOuterRenamed() {
+    return replaceInitNameInExpectedBasedOnKotlinVersion(EXPECTED_OUTER_RENAMED);
+  }
+
+  private String replaceInitNameInExpectedBasedOnKotlinVersion(String expected) {
+    return kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLIN_DEV)
+        ? expected.replace("<init>", "`<init>`")
+        : expected;
+  }
+
   @Parameterized.Parameters(name = "{0}, {1}")
   public static Collection<Object[]> data() {
     return buildParameters(
@@ -64,7 +79,7 @@
     testForRuntime(parameters)
         .addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), libJar)
         .run(parameters.getRuntime(), PKG_NESTED_REFLECT + ".MainKt")
-        .assertSuccessWithOutput(EXPECTED);
+        .assertSuccessWithOutput(getExpected());
   }
 
   @Test
@@ -83,8 +98,7 @@
             .compile()
             .inspect(inspector -> inspectPruned(inspector, true))
             .writeToZip();
-
-    runD8(mainJar, EXPECTED_OUTER_RENAMED);
+    runD8(mainJar, getExpectedOuterRenamed());
   }
 
   @Test
@@ -105,8 +119,7 @@
             .compile()
             .inspect(inspector -> inspectPruned(inspector, false))
             .writeToZip();
-
-    runD8(mainJar, EXPECTED);
+    runD8(mainJar, getExpected());
   }
 
   private void runD8(Path jar, String expected) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
new file mode 100644
index 0000000..170595a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/stringplus/StringPlusTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.stringplus;
+
+import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.KotlinCompilerTool;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 StringPlusTest extends KotlinTestBase {
+
+  private static final String MAIN = "com.android.tools.r8.kotlin.sealed.kt.StringPlusKt";
+  private static final String[] EXPECTED =
+      new String[] {
+        "Hello World!",
+        "Hello World!",
+        "StringConcat(Hello World!)",
+        "StringBuilder[Hello World!]",
+        "abc"
+      };
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}, {1}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+  }
+
+  public StringPlusTest(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
+    this.parameters = parameters;
+  }
+
+  private static final KotlinCompileMemoizer compilationResults =
+      getCompileMemoizer(getKotlinSources()).configure(KotlinCompilerTool::includeRuntime);
+
+  private static Collection<Path> getKotlinSources() {
+    try {
+      return getFilesInTestFolderRelativeToClass(StringPlusTest.class, "kt", ".kt");
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Test
+  public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+    testForRuntime(parameters)
+        .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
+        .addProgramFiles(kotlinc.getKotlinAnnotationJar())
+        .setMinApi(parameters.getApiLevel())
+        .allowAccessModification()
+        .allowDiagnosticWarningMessages()
+        .addKeepMainRule(MAIN)
+        .addKeepRules("-keep class " + MAIN + "{ void keepFor*(...); }")
+        .noMinification()
+        .compile()
+        .inspect(
+            inspector -> {
+              if (parameters.isCfRuntime()) {
+                return;
+              }
+              ClassSubject clazz = inspector.clazz(MAIN);
+              assertThat(clazz, isPresent());
+              MethodSubject methodSubject = clazz.mainMethod();
+              assertThat(methodSubject, isPresent());
+              if (kotlinParameters.isNewerThanOrEqualTo(KotlinCompilerVersion.KOTLINC_1_5_0)
+                  && kotlinParameters.isOlderThan(KotlinCompilerVersion.KOTLIN_DEV)) {
+                // TODO(b/190489514): We should be able to optimize constant stringPlus calls.
+                assertThat(methodSubject, CodeMatchers.invokesMethodWithName("stringPlus"));
+              }
+              // TODO(b/219455761): StringBuilderOptimizer fails to remove constant input.
+              assertThat(
+                  methodSubject,
+                  CodeMatchers.invokesMethodWithHolderAndName(
+                      typeName(StringBuilder.class), "append"));
+            })
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/stringplus/kt/StringPlus.kt b/src/test/java/com/android/tools/r8/kotlin/stringplus/kt/StringPlus.kt
new file mode 100644
index 0000000..4213f76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/stringplus/kt/StringPlus.kt
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.kotlin.sealed.kt
+
+fun string1() : String {
+  return "Hello "
+}
+
+fun string2() : String {
+  return "World!"
+}
+
+fun main() {
+  println(string1() + string2())
+  println(string1().plus(string2()))
+  println("StringConcat(" + string1() + string2() + ")")
+  println(StringBuilder().append("StringBuilder[").append(string1()).append(string2()).append("]"))
+  var foo = "a";
+  foo = foo + "b";
+  println(foo + "c")
+}
+
+fun keepForNoMemberRebinding() {
+  println((if (System.currentTimeMillis() > 0) "foo" else "bar")
+            + (if (System.currentTimeMillis() > 0) "baz" else "qux"))
+}
+
+fun keepForNoDoubleInlining() {
+  println((if (System.currentTimeMillis() > 0) "ini" else "mini")
+            + (if (System.currentTimeMillis() > 0) "miny" else "moe"))
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
new file mode 100644
index 0000000..66a4cdc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.proto;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ProtoNormalizationWithInstanceInitializerCollisionTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> options.testing.enableExperimentalProtoNormalization = true)
+        .enableNoHorizontalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              ClassSubject aClassSubject = inspector.clazz(A.class);
+              assertThat(aClassSubject, isPresent());
+
+              ClassSubject bClassSubject = inspector.clazz(B.class);
+              assertThat(bClassSubject, isPresent());
+
+              TypeSubject aTypeSubject = aClassSubject.asTypeSubject();
+              TypeSubject bTypeSubject = bClassSubject.asTypeSubject();
+
+              // Main.<init>(A, B) is unchanged.
+              MethodSubject initMethodSubject =
+                  mainClassSubject.initFromTypes(aTypeSubject, bTypeSubject);
+              assertThat(initMethodSubject, isPresent());
+
+              // Main.<init>(B, A) is unchanged.
+              MethodSubject otherInitMethodSubject =
+                  mainClassSubject.initFromTypes(bTypeSubject, aTypeSubject);
+              assertThat(otherInitMethodSubject, isPresent());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A", "B", "A", "B");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new Main(new A(), new B());
+      new Main(new B(), new A());
+    }
+
+    Main(A a, B b) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
+    Main(B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class A {
+
+    @Override
+    public String toString() {
+      return "A";
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class B {
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
new file mode 100644
index 0000000..a9b9af9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithParameterAnnotationsTest.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.proto;
+
+import static com.android.tools.r8.utils.codeinspector.AnnotationMatchers.hasParameterAnnotationTypes;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.MethodMatchers.hasParameters;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.TypeSubject;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+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 ProtoNormalizationWithParameterAnnotationsTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8Compat(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(Foo.class, Bar.class)
+        .addKeepRuntimeVisibleAnnotations()
+        .addOptionsModification(
+            options -> options.testing.enableExperimentalProtoNormalization = true)
+        .enableInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
+        // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
+        .noMinification()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject mainClassSubject = inspector.clazz(Main.class);
+              assertThat(mainClassSubject, isPresent());
+
+              TypeSubject aTypeSubject = inspector.clazz(A.class).asTypeSubject();
+              TypeSubject bTypeSubject = inspector.clazz(B.class).asTypeSubject();
+              TypeSubject fooTypeSubject = inspector.clazz(Foo.class).asTypeSubject();
+              TypeSubject barTypeSubject = inspector.clazz(Bar.class).asTypeSubject();
+
+              // Main.bar() has parameter annotations [@Bar, @Foo].
+              MethodSubject barMethodSubject = mainClassSubject.uniqueMethodWithName("bar");
+              assertThat(barMethodSubject, isPresent());
+              assertThat(barMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+              assertThat(
+                  barMethodSubject,
+                  hasParameterAnnotationTypes(
+                      ImmutableList.of(barTypeSubject), ImmutableList.of(fooTypeSubject)));
+
+              // Main.baz() has parameter annotations [, @Foo].
+              MethodSubject bazMethodSubject = mainClassSubject.uniqueMethodWithName("baz");
+              assertThat(bazMethodSubject, isPresent());
+              assertThat(bazMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+              assertThat(
+                  bazMethodSubject,
+                  hasParameterAnnotationTypes(
+                      ImmutableList.of(), ImmutableList.of(fooTypeSubject)));
+
+              // Main.qux() has parameter annotations [@Foo, ].
+              MethodSubject quxMethodSubject = mainClassSubject.uniqueMethodWithName("qux");
+              assertThat(quxMethodSubject, isPresent());
+              assertThat(quxMethodSubject, hasParameters(aTypeSubject, bTypeSubject));
+              assertThat(
+                  quxMethodSubject,
+                  hasParameterAnnotationTypes(
+                      ImmutableList.of(fooTypeSubject), ImmutableList.of()));
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("A", "B", "A", "B", "A", "B", "A", "B");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      foo(new A(), new B());
+      bar(new B(), new A());
+      baz(new B(), new A());
+      qux(new B(), new A());
+    }
+
+    @NeverInline
+    static void foo(A a, B b) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
+    @NeverInline
+    static void bar(@Foo B b, @Bar A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
+    @NeverInline
+    static void baz(@Foo B b, A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+
+    @NeverInline
+    static void qux(B b, @Foo A a) {
+      System.out.println(a);
+      System.out.println(b);
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class A {
+
+    @Override
+    public String toString() {
+      return "A";
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class B {
+
+    @Override
+    public String toString() {
+      return "B";
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface Foo {}
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface Bar {}
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
index 0088b3b..d9190df 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithoutSharingTest.java
@@ -41,8 +41,6 @@
             options -> options.testing.enableExperimentalProtoNormalization = true)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
-        // TODO(b/173398086): uniqueMethodWithName() does not work with proto changes.
-        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedMethodArgumentTest.java b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedMethodArgumentTest.java
new file mode 100644
index 0000000..b4d6576
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/negatedrules/NegatedMethodArgumentTest.java
@@ -0,0 +1,131 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.negatedrules;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+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 NegatedMethodArgumentTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+  @Test
+  public void testR8NegatedPrimitive() throws Exception {
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForR8(parameters.getBackend())
+                .addInnerClasses(getClass())
+                .addKeepMainRule(TestClass.class)
+                .setMinApi(parameters.getApiLevel())
+                .addKeepRules("-keep class * { void setX(!%); }")
+                .run(parameters.getRuntime(), TestClass.class)
+                .assertSuccessWithOutput(EXPECTED_OUTPUT));
+  }
+
+  @Test
+  public void testR8NegatedClass() throws Exception {
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForR8(parameters.getBackend())
+                .addInnerClasses(getClass())
+                .addKeepMainRule(TestClass.class)
+                .setMinApi(parameters.getApiLevel())
+                .addKeepRules("-keep class * { void setX(!**Producer); }")
+                .compile());
+  }
+
+  private void classPresentWithOnlyInstanceInitializer(ClassSubject subject) {
+    assertThat(subject, isPresent());
+    assertTrue(subject.allMethods().stream().allMatch(FoundMethodSubject::isInstanceInitializer));
+  }
+
+  @Test
+  public void testProguardNegatedPrimitive() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForProguard(ProguardVersion.V7_0_0)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules("-keep class * { void setX(!%); }")
+        .addKeepRules("-dontwarn **.NegatedMethodArgumentTest")
+        .noMinification()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(
+            inspector -> {
+              classPresentWithOnlyInstanceInitializer(inspector.clazz(A.class));
+              classPresentWithOnlyInstanceInitializer(inspector.clazz(B.class));
+            })
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testProguardNegatedClass() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForProguard(ProguardVersion.V7_0_0)
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .addKeepRules("-keep class * { void setX(!**.*P1); }")
+        .addKeepRules("-dontwarn **.NegatedMethodArgumentTest")
+        .noMinification()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(
+            inspector -> {
+              classPresentWithOnlyInstanceInitializer(inspector.clazz(A.class));
+              classPresentWithOnlyInstanceInitializer(inspector.clazz(B.class));
+            })
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  static class P1 {}
+
+  static class P2 {}
+
+  static class A {
+
+    public void setX(int x) {}
+
+    public void setX(P1 x) {}
+  }
+
+  static class B {
+
+    public void setX(int x) {}
+
+    public void setX(P2 x) {}
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 7f35d90..61c6fb5 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -115,7 +115,7 @@
   }
 
   @Override
-  public List<AnnotationSubject> annotations() {
+  public List<FoundAnnotationSubject> annotations() {
     throw new Unreachable("Cannot determine if an absent class has annotations");
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
index 8d91f3c..6eeab19 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
@@ -60,7 +60,7 @@
   }
 
   @Override
-  public List<AnnotationSubject> annotations() {
+  public List<FoundAnnotationSubject> annotations() {
     throw new Unreachable("Cannot determine if an absent field has annotations");
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 9be6468..a4fb7b4 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -87,6 +87,16 @@
   }
 
   @Override
+  public List<List<FoundAnnotationSubject>> getParameterAnnotations() {
+    throw new Unreachable("Cannot get the parameter annotations for an absent method");
+  }
+
+  @Override
+  public List<FoundAnnotationSubject> getParameterAnnotations(int index) {
+    throw new Unreachable("Cannot get the parameter annotations for an absent method");
+  }
+
+  @Override
   public ProgramMethod getProgramMethod() {
     return null;
   }
@@ -127,7 +137,7 @@
   }
 
   @Override
-  public List<AnnotationSubject> annotations() {
+  public List<FoundAnnotationSubject> annotations() {
     throw new Unreachable("Cannot determine if an absent method has annotations");
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationMatchers.java
new file mode 100644
index 0000000..8965f41
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AnnotationMatchers.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.codeinspector;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class AnnotationMatchers {
+
+  public static Matcher<MethodSubject> hasParameterAnnotationTypes(
+      List<TypeSubject>... typeSubjects) {
+    return hasParameterAnnotationTypes(Arrays.asList(typeSubjects));
+  }
+
+  public static Matcher<MethodSubject> hasParameterAnnotationTypes(
+      List<List<TypeSubject>> typeSubjects) {
+    return new TypeSafeMatcher<MethodSubject>() {
+
+      @Override
+      protected boolean matchesSafely(MethodSubject subject) {
+        List<List<FoundAnnotationSubject>> parameterAnnotations = subject.getParameterAnnotations();
+        if (parameterAnnotations.size() != typeSubjects.size()) {
+          return false;
+        }
+        for (int parameterIndex = 0;
+            parameterIndex < parameterAnnotations.size();
+            parameterIndex++) {
+          List<FoundAnnotationSubject> parameterAnnotationsForParameter =
+              parameterAnnotations.get(parameterIndex);
+          List<TypeSubject> typeSubjectsForParameter = typeSubjects.get(parameterIndex);
+          if (!hasAnnotationTypes(typeSubjectsForParameter)
+              .matches(parameterAnnotationsForParameter)) {
+            return false;
+          }
+        }
+        return true;
+      }
+
+      @Override
+      public void describeTo(Description description) {
+        description.appendText(
+            "has parameter annotation types ["
+                + StringUtils.join(
+                    "; ",
+                    typeSubjects,
+                    typeSubjectsForParameter ->
+                        StringUtils.join(", ", typeSubjectsForParameter, TypeSubject::getTypeName))
+                + "]");
+      }
+
+      @Override
+      public void describeMismatchSafely(MethodSubject subject, Description description) {
+        description.appendText("method did not");
+      }
+    };
+  }
+
+  public static Matcher<List<FoundAnnotationSubject>> hasAnnotationTypes(
+      TypeSubject... typeSubjects) {
+    return hasAnnotationTypes(Arrays.asList(typeSubjects));
+  }
+
+  public static Matcher<List<FoundAnnotationSubject>> hasAnnotationTypes(
+      List<TypeSubject> typeSubjects) {
+    return new TypeSafeMatcher<List<FoundAnnotationSubject>>() {
+
+      @Override
+      protected boolean matchesSafely(List<FoundAnnotationSubject> subjects) {
+        if (subjects.size() != typeSubjects.size()) {
+          return false;
+        }
+        for (int i = 0; i < subjects.size(); i++) {
+          FoundAnnotationSubject subject = subjects.get(i);
+          TypeSubject typeSubject = typeSubjects.get(i);
+          if (!subject.getType().equals(typeSubject)) {
+            return false;
+          }
+        }
+        return true;
+      }
+
+      @Override
+      public void describeTo(Description description) {
+        description.appendText(
+            "has annotation types ["
+                + StringUtils.join(", ", typeSubjects, TypeSubject::getTypeName)
+                + "]");
+      }
+
+      @Override
+      public void describeMismatchSafely(
+          List<FoundAnnotationSubject> subjects, Description description) {
+        description.appendText("annotations did not");
+      }
+    };
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
index 754b470..2c38d35 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
@@ -10,7 +10,7 @@
 
 public abstract class ClassOrMemberSubject extends Subject {
 
-  public abstract List<AnnotationSubject> annotations();
+  public abstract List<FoundAnnotationSubject> annotations();
 
   public abstract AnnotationSubject annotation(String name);
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 30b3fb0..d604156 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -26,6 +26,7 @@
 import java.util.List;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 import kotlinx.metadata.jvm.KotlinClassMetadata;
 import org.junit.rules.TemporaryFolder;
 
@@ -128,6 +129,14 @@
     return init(Arrays.asList(parameters));
   }
 
+  public MethodSubject initFromTypes(List<TypeSubject> parameters) {
+    return init(parameters.stream().map(TypeSubject::getTypeName).collect(Collectors.toList()));
+  }
+
+  public MethodSubject initFromTypes(TypeSubject... parameters) {
+    return initFromTypes(Arrays.asList(parameters));
+  }
+
   public MethodSubject method(MethodSignature signature) {
     return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
index d06b289..e73c25a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
@@ -14,13 +14,22 @@
 public class FoundAnnotationSubject extends AnnotationSubject {
 
   private final DexAnnotation annotation;
+  private final CodeInspector codeInspector;
 
-  FoundAnnotationSubject(DexAnnotation annotation) {
+  FoundAnnotationSubject(DexAnnotation annotation, CodeInspector codeInspector) {
     this.annotation = annotation;
+    this.codeInspector = codeInspector;
   }
 
-  public static List<AnnotationSubject> listFromDex(DexAnnotationSet annotations) {
-    return ListUtils.map(annotations.annotations, FoundAnnotationSubject::new);
+  public static List<FoundAnnotationSubject> listFromDex(
+      DexAnnotationSet annotations, CodeInspector codeInspector) {
+    return ListUtils.map(
+        annotations.annotations,
+        annotation -> new FoundAnnotationSubject(annotation, codeInspector));
+  }
+
+  public TypeSubject getType() {
+    return new TypeSubject(codeInspector, annotation.getAnnotationType());
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index cdc420e..fbf91d6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -359,8 +359,8 @@
   }
 
   @Override
-  public List<AnnotationSubject> annotations() {
-    return FoundAnnotationSubject.listFromDex(dexClass.annotations());
+  public List<FoundAnnotationSubject> annotations() {
+    return FoundAnnotationSubject.listFromDex(dexClass.annotations(), codeInspector);
   }
 
   @Override
@@ -372,7 +372,7 @@
     DexAnnotation annotation = codeInspector.findAnnotation(name, dexClass.annotations());
     return annotation == null
         ? new AbsentAnnotationSubject()
-        : new FoundAnnotationSubject(annotation);
+        : new FoundAnnotationSubject(annotation, codeInspector);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index 2ea0107..c2daded 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -114,7 +114,7 @@
   }
 
   @Override
-  public List<AnnotationSubject> annotations() {
+  public List<FoundAnnotationSubject> annotations() {
     throw new Unimplemented();
   }
 
@@ -123,7 +123,7 @@
     DexAnnotation annotation = codeInspector.findAnnotation(name, dexField.annotations());
     return annotation == null
         ? new AbsentAnnotationSubject()
-        : new FoundAnnotationSubject(annotation);
+        : new FoundAnnotationSubject(annotation, codeInspector);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index b99b090..b33f4a3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -42,6 +42,7 @@
 import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
@@ -136,6 +137,24 @@
   }
 
   @Override
+  public List<FoundAnnotationSubject> getParameterAnnotations(int index) {
+    return FoundAnnotationSubject.listFromDex(
+        getMethod().getParameterAnnotation(index), codeInspector);
+  }
+
+  @Override
+  public List<List<FoundAnnotationSubject>> getParameterAnnotations() {
+    List<List<FoundAnnotationSubject>> parameterAnnotations =
+        new ArrayList<>(getMethod().getParameters().size());
+    for (int parameterIndex = 0;
+        parameterIndex < getMethod().getParameters().size();
+        parameterIndex++) {
+      parameterAnnotations.add(getParameterAnnotations(parameterIndex));
+    }
+    return parameterAnnotations;
+  }
+
+  @Override
   public ProgramMethod getProgramMethod() {
     return new ProgramMethod(clazz.getDexProgramClass(), getMethod());
   }
@@ -343,8 +362,8 @@
   }
 
   @Override
-  public List<AnnotationSubject> annotations() {
-    return FoundAnnotationSubject.listFromDex(dexMethod.annotations());
+  public List<FoundAnnotationSubject> annotations() {
+    return FoundAnnotationSubject.listFromDex(dexMethod.annotations(), codeInspector);
   }
 
   @Override
@@ -352,7 +371,7 @@
     DexAnnotation annotation = codeInspector.findAnnotation(name, dexMethod.annotations());
     return annotation == null
         ? new AbsentAnnotationSubject()
-        : new FoundAnnotationSubject(annotation);
+        : new FoundAnnotationSubject(annotation, codeInspector);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 3b7fe05..dda3e35 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -56,6 +56,10 @@
 
   public abstract List<TypeSubject> getParameters();
 
+  public abstract List<List<FoundAnnotationSubject>> getParameterAnnotations();
+
+  public abstract List<FoundAnnotationSubject> getParameterAnnotations(int index);
+
   public abstract ProgramMethod getProgramMethod();
 
   public Iterator<InstructionSubject> iterateInstructions() {
diff --git a/third_party/google/google-java-format/1.14.0.tar.gz.sha1 b/third_party/google/google-java-format/1.14.0.tar.gz.sha1
new file mode 100644
index 0000000..4989c27
--- /dev/null
+++ b/third_party/google/google-java-format/1.14.0.tar.gz.sha1
@@ -0,0 +1 @@
+b26f4b8c825401d198d54d5e230779fe7fc3c132
\ No newline at end of file
diff --git a/tools/download_kotlin_dev.py b/tools/download_kotlin_dev.py
index c6c127e..6360cd2 100755
--- a/tools/download_kotlin_dev.py
+++ b/tools/download_kotlin_dev.py
@@ -3,22 +3,28 @@
 # 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.
 
-from HTMLParser import HTMLParser
+import utils
+if utils.is_python3():
+  from html.parser import HTMLParser
+  import urllib.request
+  url_request = urllib.request
+else:
+  from HTMLParser import HTMLParser
+  import urllib
+  url_request = urllib
 import os
 import sys
-import urllib
-import utils
 
 JETBRAINS_KOTLIN_MAVEN_URL = "https://maven.pkg.jetbrains.space/kotlin/p/" \
                              "kotlin/bootstrap/org/jetbrains/kotlin/"
 KOTLIN_RELEASE_URL = JETBRAINS_KOTLIN_MAVEN_URL + "kotlin-compiler/"
 
 def download_newest():
-  response = urllib.urlopen(KOTLIN_RELEASE_URL)
+  response = url_request.urlopen(KOTLIN_RELEASE_URL)
   if response.getcode() != 200:
     raise Exception('Url: %s \n returned %s'
                     % (KOTLIN_RELEASE_URL, response.getcode()))
-  content = response.read()
+  content = str(response.read())
   release_candidates = []
 
   class HTMLContentParser(HTMLParser):
@@ -70,7 +76,7 @@
 
 def download_and_save(url, path, name):
   print('Downloading: ' + url)
-  urllib.urlretrieve(url, os.path.join(path, name))
+  url_request.urlretrieve(url, os.path.join(path, name))
 
 
 if __name__ == '__main__':
diff --git a/tools/fmt-diff.py b/tools/fmt-diff.py
index fc08c80..cda33f9 100755
--- a/tools/fmt-diff.py
+++ b/tools/fmt-diff.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright (c) 2020, 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.
@@ -13,8 +13,10 @@
 
 GOOGLE_JAVA_FORMAT_DIFF = os.path.join(
     utils.THIRD_PARTY,
+    'google',
     'google-java-format',
-    'google-java-format-google-java-format-1.7',
+    '1.14.0',
+    'google-java-format-1.14.0',
     'scripts',
     'google-java-format-diff.py')
 
diff --git a/tools/test.py b/tools/test.py
index e822efe..56f30e7 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -15,13 +15,17 @@
 import shutil
 import subprocess
 import sys
-import thread
 import time
 import uuid
 
 import gradle
 import utils
 
+if utils.is_python3():
+  import threading
+else:
+  import thread
+
 ALL_ART_VMS = [
     "default",
     "13.0.0",
@@ -365,7 +369,7 @@
     if args is None:
       return 1
     if len(args) == 0:
-      print "No failing tests"
+      print("No failing tests")
       return 0
   # Test filtering. Must always follow the 'test' task.
   for testFilter in args:
@@ -392,7 +396,14 @@
     print_stacks_timeout = (print_stacks_timeout
                             if print_stacks_timeout != -1
                             else TIMEOUT_HANDLER_PERIOD)
-    thread.start_new_thread(timeout_handler, (timestamp_file, print_stacks_timeout,))
+    if utils.is_python3():
+      threading.Thread(
+          target=timeout_handler,
+          args=(timestamp_file, print_stacks_timeout),
+          daemon=True).start()
+    else:
+      thread.start_new_thread(
+          timeout_handler, (timestamp_file, print_stacks_timeout,))
   rotate_test_reports()
 
   if options.only_jctf:
@@ -403,7 +414,7 @@
   # Now run tests on selected runtime(s).
   if options.runtimes:
     if options.dex_vm != 'default':
-      print 'Unexpected runtimes and dex_vm argument: ' + options.dex_vm
+      print('Unexpected runtimes and dex_vm argument: ' + options.dex_vm)
       sys.exit(1)
     if options.runtimes == 'empty':
       # Set runtimes with no content will configure no runtimes.
@@ -417,9 +428,9 @@
       for prefix in prefixes:
         matches = [ rt for rt in VALID_RUNTIMES if rt.startswith(prefix) ]
         if len(matches) == 0:
-          print "Invalid runtime prefix '%s'." % prefix
-          print "Must be just 'all', 'empty'," \
-                " or a prefix of %s" % ', '.join(VALID_RUNTIMES)
+          print("Invalid runtime prefix '%s'." % prefix)
+          print("Must be just 'all', 'empty'," \
+                " or a prefix of %s" % ', '.join(VALID_RUNTIMES))
           sys.exit(1)
         runtimes.extend(matches)
       gradle_args.append('-Pruntimes=%s' % ':'.join(runtimes))
@@ -499,7 +510,7 @@
     last_timestamp = new_timestamp
 
 def report_dir_path(index):
-  if index is 0:
+  if index == 0:
     return REPORTS_PATH
   return '%s%d' % (REPORTS_PATH, index)
 
@@ -521,7 +532,7 @@
 
 def compute_failed_tests(args):
   if len(args) > 1:
-    print "Running with --failed can take an optional path to a report index (or report number)."
+    print("Running with --failed can take an optional path to a report index (or report number).")
     return None
   report = report_index_path(0)
   # If the default report does not exist, fall back to the previous report as it may be a failed
@@ -539,9 +550,9 @@
       # if integer parsing failed assume it is a report file path.
       report = args[0]
   if not os.path.exists(report):
-    print "Can't re-run failing, no report at:", report
+    print("Can't re-run failing, no report at:", report)
     return None
-  print "Reading failed tests in", report
+  print("Reading failed tests in", report)
   failing = set()
   inFailedSection = False
   for line in file(report):