Merge commit 'a2f77004a2451840c4f5b504f4a4c0126bf16a7a' into dev-release
diff --git a/build.gradle b/build.gradle
index bde2f60..49d3ecf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,7 +33,7 @@
 
 ext {
     androidSupportVersion = '25.4.0'
-    asmVersion = '9.3'  // When updating update tools/asmifier.py, build.src and Toolhelper as well.
+    asmVersion = '9.4'  // When updating update tools/asmifier.py, build.src and Toolhelper as well.
     espressoVersion = '3.0.0'
     fastutilVersion = '7.2.0'
     guavaVersion = '30.1.1-jre'
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 81527b4..3f4e21f 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -8,7 +8,7 @@
     mavenCentral()
 }
 ext {
-    asmVersion = '9.3'
+    asmVersion = '9.4'
 }
 
 dependencies {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
index 01c493f..2d5eb60 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -25,9 +25,13 @@
 @Target(ElementType.ANNOTATION_TYPE)
 @Retention(RetentionPolicy.CLASS)
 public @interface KeepCondition {
+  String className() default "";
+
   Class<?> classConstant() default Object.class;
 
-  String classTypeName() default "";
+  String extendsClassName() default "";
+
+  Class<?> extendsClassConstant() default Object.class;
 
   String methodName() default "";
 
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
index 0018dfc..02eb5df 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
@@ -19,9 +19,20 @@
     return classTypeName.replace('.', '/');
   }
 
+  public static boolean isKeepAnnotation(String descriptor, boolean visible) {
+    if (visible) {
+      return false;
+    }
+    return descriptor.equals(Edge.DESCRIPTOR)
+        || descriptor.equals(UsesReflection.DESCRIPTOR)
+        || descriptor.equals(Condition.DESCRIPTOR)
+        || descriptor.equals(Target.DESCRIPTOR);
+  }
+
   public static final class Edge {
     public static final Class<KeepEdge> CLASS = KeepEdge.class;
     public static final String DESCRIPTOR = getDescriptor(CLASS);
+    public static final String description = "description";
     public static final String preconditions = "preconditions";
     public static final String consequences = "consequences";
   }
@@ -30,14 +41,19 @@
     public static final Class<com.android.tools.r8.keepanno.annotations.UsesReflection> CLASS =
         com.android.tools.r8.keepanno.annotations.UsesReflection.class;
     public static final String DESCRIPTOR = getDescriptor(CLASS);
+    public static final String description = "description";
     public static final String value = "value";
     public static final String additionalPreconditions = "additionalPreconditions";
   }
 
   // Implicit hidden item which is "super type" of Condition and Target.
   public static final class Item {
+    public static final String className = "className";
     public static final String classConstant = "classConstant";
 
+    public static final String extendsClassName = "extendsClassName";
+    public static final String extendsClassConstant = "extendsClassConstant";
+
     public static final String methodName = "methodName";
     public static final String methodReturnType = "methodReturnType";
     public static final String methodParameters = "methodParameters";
@@ -48,6 +64,12 @@
     // Default values for the optional entries. The defaults should be chosen such that they do
     // not coincide with any actual valid value. E.g., the empty string in place of a name or type.
     // These must be 1:1 with the value defined on the actual annotation definition.
+    public static final String classNameDefault = "";
+    public static final Class<?> classConstantDefault = Object.class;
+
+    public static final String extendsClassNameDefault = "";
+    public static final Class<?> extendsClassConstantDefault = Object.class;
+
     public static final String methodNameDefaultValue = "";
     public static final String methodReturnTypeDefaultValue = "";
     public static final String[] methodParametersDefaultValue = new String[] {""};
@@ -64,5 +86,19 @@
   public static final class Target {
     public static final Class<KeepTarget> CLASS = KeepTarget.class;
     public static final String DESCRIPTOR = getDescriptor(CLASS);
+
+    public static final String allow = "allow";
+    public static final String disallow = "disallow";
+  }
+
+  public static final class Option {
+    public static final Class<KeepOption> CLASS = KeepOption.class;
+    public static final String DESCRIPTOR = getDescriptor(CLASS);
+
+    public static final String SHRINKING = "SHRINKING";
+    public static final String OBFUSCATION = "OBFUSCATION";
+    public static final String OPTIMIZATION = "OPTIMIZATION";
+    public static final String ACCESS_MODIFICATION = "ACCESS_MODIFICATION";
+    public static final String ANNOTATION_REMOVAL = "ANNOTATION_REMOVAL";
   }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
index fb9a772..329b153 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
@@ -8,9 +8,11 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
 @Retention(RetentionPolicy.CLASS)
 public @interface KeepEdge {
+  String description() default "";
+
   KeepCondition[] preconditions() default {};
 
   KeepTarget[] consequences();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepOption.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepOption.java
new file mode 100644
index 0000000..e3c6d31
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepOption.java
@@ -0,0 +1,12 @@
+// 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.keepanno.annotations;
+
+public enum KeepOption {
+  SHRINKING,
+  OPTIMIZATION,
+  OBFUSCATION,
+  ACCESS_MODIFICATION,
+  ANNOTATION_REMOVAL,
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
index 73559d6..e4cb263 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -25,9 +25,22 @@
 @Target(ElementType.ANNOTATION_TYPE)
 @Retention(RetentionPolicy.CLASS)
 public @interface KeepTarget {
+
+  // KeepTarget only content (keep options) =========
+
+  KeepOption[] allow() default {};
+
+  KeepOption[] disallow() default {};
+
+  // Shared KeepItem content ========================
+
+  String className() default "";
+
   Class<?> classConstant() default Object.class;
 
-  String classTypeName() default "";
+  String extendsClassName() default "";
+
+  Class<?> extendsClassConstant() default Object.class;
 
   String methodName() default "";
 
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
index 22fcb36..0af2ec6 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
@@ -53,10 +53,13 @@
  *   }
  * </pre>
  */
-@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
 @Retention(RetentionPolicy.CLASS)
 public @interface UsesReflection {
+  String description() default "";
+
   KeepTarget[] value();
 
   KeepCondition[] additionalPreconditions() default {};
+
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index 672a99e7..5d23d5d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -7,26 +7,32 @@
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Condition;
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Item;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Option;
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
 import com.android.tools.r8.keepanno.ast.KeepCondition;
 import com.android.tools.r8.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo;
+import com.android.tools.r8.keepanno.ast.KeepExtendsPattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern;
 import com.android.tools.r8.keepanno.ast.KeepItemPattern;
-import com.android.tools.r8.keepanno.ast.KeepItemPattern.Builder;
+import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern;
+import com.android.tools.r8.keepanno.ast.KeepOptions;
+import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
 import com.android.tools.r8.keepanno.ast.KeepPreconditions;
 import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepTarget;
 import com.android.tools.r8.keepanno.ast.KeepTypePattern;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -77,23 +83,27 @@
 
     @Override
     public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+      // Skip any visible annotations as @KeepEdge is not runtime visible.
       if (visible) {
         return null;
       }
-      // Skip any visible annotations as @KeepEdge is not runtime visible.
       if (descriptor.equals(Edge.DESCRIPTOR)) {
-        return new KeepEdgeVisitor(parent);
+        return new KeepEdgeVisitor(parent, this::setContext);
       }
       if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
         KeepItemPattern classItem =
             KeepItemPattern.builder()
                 .setClassPattern(KeepQualifiedClassNamePattern.exact(className))
                 .build();
-        return new UsesReflectionVisitor(parent, classItem);
+        return new UsesReflectionVisitor(parent, this::setContext, classItem);
       }
       return null;
     }
 
+    private void setContext(KeepEdgeMetaInfo.Builder builder) {
+      builder.setContextFromClassDescriptor(KeepEdgeReaderUtils.javaTypeToDescriptor(className));
+    }
+
     @Override
     public MethodVisitor visitMethod(
         int access, String name, String descriptor, String signature, String[] exceptions) {
@@ -152,13 +162,18 @@
         return null;
       }
       if (descriptor.equals(Edge.DESCRIPTOR)) {
-        return new KeepEdgeVisitor(parent);
+        return new KeepEdgeVisitor(parent, this::setContext);
       }
       if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
-        return new UsesReflectionVisitor(parent, createItemContext());
+        return new UsesReflectionVisitor(parent, this::setContext, createItemContext());
       }
       return null;
     }
+
+    private void setContext(KeepEdgeMetaInfo.Builder builder) {
+      builder.setContextFromMethodDescriptor(
+          KeepEdgeReaderUtils.javaTypeToDescriptor(className), methodName, methodDescriptor);
+    }
   }
 
   private static class KeepEdgeFieldVisitor extends FieldVisitor {
@@ -189,6 +204,11 @@
           .build();
     }
 
+    private void setContext(KeepEdgeMetaInfo.Builder builder) {
+      builder.setContextFromFieldDescriptor(
+          KeepEdgeReaderUtils.javaTypeToDescriptor(className), fieldName, fieldDescriptor);
+    }
+
     @Override
     public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
       // Skip any visible annotations as @KeepEdge is not runtime visible.
@@ -196,10 +216,10 @@
         return null;
       }
       if (descriptor.equals(Edge.DESCRIPTOR)) {
-        return new KeepEdgeVisitor(parent);
+        return new KeepEdgeVisitor(parent, this::setContext);
       }
       if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
-        return new UsesReflectionVisitor(parent, createItemContext());
+        return new UsesReflectionVisitor(parent, this::setContext, createItemContext());
       }
       return null;
     }
@@ -240,9 +260,20 @@
   private static class KeepEdgeVisitor extends AnnotationVisitorBase {
     private final Parent<KeepEdge> parent;
     private final KeepEdge.Builder builder = KeepEdge.builder();
+    private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
 
-    KeepEdgeVisitor(Parent<KeepEdge> parent) {
+    KeepEdgeVisitor(Parent<KeepEdge> parent, Consumer<KeepEdgeMetaInfo.Builder> addContext) {
       this.parent = parent;
+      addContext.accept(metaInfoBuilder);
+    }
+
+    @Override
+    public void visit(String name, Object value) {
+      if (name.equals(Edge.description) && value instanceof String) {
+        metaInfoBuilder.setDescription((String) value);
+        return;
+      }
+      super.visit(name, value);
     }
 
     @Override
@@ -258,7 +289,7 @@
 
     @Override
     public void visitEnd() {
-      parent.accept(builder.build());
+      parent.accept(builder.setMetaInfo(metaInfoBuilder.build()).build());
     }
   }
 
@@ -266,10 +297,24 @@
     private final Parent<KeepEdge> parent;
     private final KeepEdge.Builder builder = KeepEdge.builder();
     private final KeepPreconditions.Builder preconditions = KeepPreconditions.builder();
+    private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
 
-    UsesReflectionVisitor(Parent<KeepEdge> parent, KeepItemPattern context) {
+    UsesReflectionVisitor(
+        Parent<KeepEdge> parent,
+        Consumer<KeepEdgeMetaInfo.Builder> addContext,
+        KeepItemPattern context) {
       this.parent = parent;
       preconditions.addCondition(KeepCondition.builder().setItem(context).build());
+      addContext.accept(metaInfoBuilder);
+    }
+
+    @Override
+    public void visit(String name, Object value) {
+      if (name.equals(Edge.description) && value instanceof String) {
+        metaInfoBuilder.setDescription((String) value);
+        return;
+      }
+      super.visit(name, value);
     }
 
     @Override
@@ -288,7 +333,11 @@
 
     @Override
     public void visitEnd() {
-      parent.accept(builder.setPreconditions(preconditions.build()).build());
+      parent.accept(
+          builder
+              .setMetaInfo(metaInfoBuilder.build())
+              .setPreconditions(preconditions.build())
+              .build());
     }
   }
 
@@ -325,7 +374,7 @@
     @Override
     public AnnotationVisitor visitAnnotation(String name, String descriptor) {
       if (descriptor.equals(Target.DESCRIPTOR)) {
-        return new KeepTargetVisitor(builder::addTarget);
+        return KeepTargetVisitor.create(builder::addTarget);
       }
       return super.visitAnnotation(name, descriptor);
     }
@@ -336,80 +385,176 @@
     }
   }
 
-  private abstract static class KeepItemVisitorBase extends AnnotationVisitorBase {
-    private final Parent<KeepItemPattern> parent;
+  private abstract static class Declaration<T> {
+    abstract String kind();
 
-    private KeepQualifiedClassNamePattern classNamePattern = null;
-    private KeepMethodPattern.Builder lazyMethodBuilder = null;
-    private KeepFieldPattern.Builder lazyFieldBuilder = null;
+    abstract T getValue();
 
-    public KeepItemVisitorBase(Parent<KeepItemPattern> parent) {
-      this.parent = parent;
+    boolean tryParse(String name, Object value) {
+      return false;
     }
 
-    private KeepMethodPattern.Builder methodBuilder() {
-      if (lazyFieldBuilder != null) {
-        throw new KeepEdgeException("Cannot define both a field and a method pattern");
-      }
-      if (lazyMethodBuilder == null) {
-        lazyMethodBuilder = KeepMethodPattern.builder();
-      }
-      return lazyMethodBuilder;
+    AnnotationVisitor tryParseArray(String name, Consumer<T> onValue) {
+      return null;
+    }
+  }
+
+  private abstract static class SingleDeclaration<T> extends Declaration<T> {
+    private String declarationName = null;
+    private T declarationValue = null;
+    private AnnotationVisitor declarationVisitor = null;
+
+    abstract T getDefaultValue();
+
+    abstract T parse(String name, Object value);
+
+    AnnotationVisitor parseArray(String name, Consumer<T> setValue) {
+      return null;
     }
 
-    private KeepFieldPattern.Builder fieldBuilder() {
-      if (lazyMethodBuilder != null) {
-        throw new KeepEdgeException("Cannot define both a field and a method pattern");
-      }
-      if (lazyFieldBuilder == null) {
-        lazyFieldBuilder = KeepFieldPattern.builder();
-      }
-      return lazyFieldBuilder;
+    private boolean hasDeclaration() {
+      return declarationValue != null || declarationVisitor != null;
+    }
+
+    private void error(String name) {
+      throw new KeepEdgeException(
+          "Multiple declarations defining "
+              + kind()
+              + ": '"
+              + declarationName
+              + "' and '"
+              + name
+              + "'");
     }
 
     @Override
-    public void visit(String name, Object value) {
-      if (name.equals(Item.classConstant) && value instanceof Type) {
-        classNamePattern = KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
-        return;
+    public final T getValue() {
+      return declarationValue == null ? getDefaultValue() : declarationValue;
+    }
+
+    @Override
+    final boolean tryParse(String name, Object value) {
+      T result = parse(name, value);
+      if (result != null) {
+        if (hasDeclaration()) {
+          error(name);
+        }
+        declarationName = name;
+        declarationValue = result;
+        return true;
       }
+      return false;
+    }
+
+    @Override
+    AnnotationVisitor tryParseArray(String name, Consumer<T> setValue) {
+      AnnotationVisitor visitor = parseArray(name, setValue.andThen(v -> declarationValue = v));
+      if (visitor != null) {
+        if (hasDeclaration()) {
+          error(name);
+        }
+        declarationName = name;
+        declarationVisitor = visitor;
+        return visitor;
+      }
+      return null;
+    }
+  }
+
+  private static class ClassDeclaration extends SingleDeclaration<KeepQualifiedClassNamePattern> {
+    @Override
+    String kind() {
+      return "class";
+    }
+
+    @Override
+    KeepQualifiedClassNamePattern getDefaultValue() {
+      return KeepQualifiedClassNamePattern.any();
+    }
+
+    @Override
+    KeepQualifiedClassNamePattern parse(String name, Object value) {
+      if (name.equals(Item.classConstant) && value instanceof Type) {
+        return KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
+      }
+      if (name.equals(Item.className) && value instanceof String) {
+        return KeepQualifiedClassNamePattern.exact(((String) value));
+      }
+      return null;
+    }
+  }
+
+  private static class ExtendsDeclaration extends SingleDeclaration<KeepExtendsPattern> {
+
+    @Override
+    String kind() {
+      return "extends";
+    }
+
+    @Override
+    KeepExtendsPattern getDefaultValue() {
+      return KeepExtendsPattern.any();
+    }
+
+    @Override
+    KeepExtendsPattern parse(String name, Object value) {
+      if (name.equals(Item.extendsClassConstant) && value instanceof Type) {
+        return KeepExtendsPattern.builder()
+            .classPattern(KeepQualifiedClassNamePattern.exact(((Type) value).getClassName()))
+            .build();
+      }
+      if (name.equals(Item.extendsClassName) && value instanceof String) {
+        return KeepExtendsPattern.builder()
+            .classPattern(KeepQualifiedClassNamePattern.exact(((String) value)))
+            .build();
+      }
+      return null;
+    }
+  }
+
+  private static class MethodDeclaration extends Declaration<KeepMethodPattern> {
+
+    private KeepMethodPattern.Builder builder = null;
+
+    private KeepMethodPattern.Builder getBuilder() {
+      if (builder == null) {
+        builder = KeepMethodPattern.builder();
+      }
+      return builder;
+    }
+
+    @Override
+    String kind() {
+      return "method";
+    }
+
+    @Override
+    KeepMethodPattern getValue() {
+      return builder != null ? builder.build() : null;
+    }
+
+    @Override
+    boolean tryParse(String name, Object value) {
       if (name.equals(Item.methodName) && value instanceof String) {
         String methodName = (String) value;
         if (!Item.methodNameDefaultValue.equals(methodName)) {
-          methodBuilder().setNamePattern(KeepMethodNamePattern.exact(methodName));
+          getBuilder().setNamePattern(KeepMethodNamePattern.exact(methodName));
         }
-        return;
+        return true;
       }
       if (name.equals(Item.methodReturnType) && value instanceof String) {
         String returnType = (String) value;
         if (!Item.methodReturnTypeDefaultValue.equals(returnType)) {
-          methodBuilder()
+          getBuilder()
               .setReturnTypePattern(KeepEdgeReaderUtils.methodReturnTypeFromString(returnType));
         }
-        return;
+        return true;
       }
-      if (name.equals(Item.fieldName) && value instanceof String) {
-        String fieldName = (String) value;
-        if (!Item.fieldNameDefaultValue.equals(fieldName)) {
-          fieldBuilder().setNamePattern(KeepFieldNamePattern.exact(fieldName));
-        }
-        return;
-      }
-      if (name.equals(Item.fieldType) && value instanceof String) {
-        String fieldType = (String) value;
-        if (!Item.fieldTypeDefaultValue.equals(fieldType)) {
-          fieldBuilder()
-              .setTypePattern(
-                  KeepFieldTypePattern.fromType(
-                      KeepEdgeReaderUtils.typePatternFromString(fieldType)));
-        }
-        return;
-      }
-      super.visit(name, value);
+      return false;
     }
 
     @Override
-    public AnnotationVisitor visitArray(String name) {
+    AnnotationVisitor tryParseArray(String name, Consumer<KeepMethodPattern> ignored) {
       if (name.equals(Item.methodParameters)) {
         return new StringArrayVisitor(
             params -> {
@@ -420,26 +565,136 @@
               for (String param : params) {
                 builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
               }
-              methodBuilder().setParametersPattern(builder.build());
+              KeepMethodParametersPattern result = builder.build();
+              getBuilder().setParametersPattern(result);
             });
       }
+      return null;
+    }
+  }
+
+  private static class FieldDeclaration extends Declaration<KeepFieldPattern> {
+
+    private KeepFieldPattern.Builder builder = null;
+
+    private KeepFieldPattern.Builder getBuilder() {
+      if (builder == null) {
+        builder = KeepFieldPattern.builder();
+      }
+      return builder;
+    }
+
+    @Override
+    String kind() {
+      return "field";
+    }
+
+    @Override
+    KeepFieldPattern getValue() {
+      return builder != null ? builder.build() : null;
+    }
+
+    @Override
+    boolean tryParse(String name, Object value) {
+      if (name.equals(Item.fieldName) && value instanceof String) {
+        String fieldName = (String) value;
+        if (!Item.fieldNameDefaultValue.equals(fieldName)) {
+          getBuilder().setNamePattern(KeepFieldNamePattern.exact(fieldName));
+        }
+        return true;
+      }
+      if (name.equals(Item.fieldType) && value instanceof String) {
+        String fieldType = (String) value;
+        if (!Item.fieldTypeDefaultValue.equals(fieldType)) {
+          getBuilder()
+              .setTypePattern(
+                  KeepFieldTypePattern.fromType(
+                      KeepEdgeReaderUtils.typePatternFromString(fieldType)));
+        }
+        return true;
+      }
+      return false;
+    }
+  }
+
+  private static class MemberDeclaration extends Declaration<KeepMemberPattern> {
+
+    private MethodDeclaration methodDeclaration = new MethodDeclaration();
+    private FieldDeclaration fieldDeclaration = new FieldDeclaration();
+
+    @Override
+    String kind() {
+      return "member";
+    }
+
+    @Override
+    public KeepMemberPattern getValue() {
+      KeepMethodPattern method = methodDeclaration.getValue();
+      KeepFieldPattern field = fieldDeclaration.getValue();
+      if (method != null && field != null) {
+        throw new KeepEdgeException("Cannot define both a field and a method pattern");
+      }
+      if (method != null) {
+        return method;
+      }
+      if (field != null) {
+        return field;
+      }
+      return KeepMemberPattern.none();
+    }
+
+    @Override
+    boolean tryParse(String name, Object value) {
+      return methodDeclaration.tryParse(name, value) || fieldDeclaration.tryParse(name, value);
+    }
+
+    @Override
+    AnnotationVisitor tryParseArray(String name, Consumer<KeepMemberPattern> ignored) {
+      AnnotationVisitor visitor = methodDeclaration.tryParseArray(name, v -> {});
+      if (visitor != null) {
+        return visitor;
+      }
+      return fieldDeclaration.tryParseArray(name, v -> {});
+    }
+  }
+
+  private abstract static class KeepItemVisitorBase extends AnnotationVisitorBase {
+    private final Parent<KeepItemPattern> parent;
+    private final ClassDeclaration classDeclaration = new ClassDeclaration();
+    private final ExtendsDeclaration extendsDeclaration = new ExtendsDeclaration();
+    private final MemberDeclaration memberDeclaration = new MemberDeclaration();
+
+    public KeepItemVisitorBase(Parent<KeepItemPattern> parent) {
+      this.parent = parent;
+    }
+
+    @Override
+    public void visit(String name, Object value) {
+      if (classDeclaration.tryParse(name, value)
+          || extendsDeclaration.tryParse(name, value)
+          || memberDeclaration.tryParse(name, value)) {
+        return;
+      }
+      super.visit(name, value);
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(String name) {
+      AnnotationVisitor visitor = memberDeclaration.tryParseArray(name, v -> {});
+      if (visitor != null) {
+        return visitor;
+      }
       return super.visitArray(name);
     }
 
     @Override
     public void visitEnd() {
-      assert lazyMethodBuilder == null || lazyFieldBuilder == null;
-      Builder itemBuilder = KeepItemPattern.builder();
-      if (classNamePattern != null) {
-        itemBuilder.setClassPattern(classNamePattern);
-      }
-      if (lazyMethodBuilder != null) {
-        itemBuilder.setMemberPattern(lazyMethodBuilder.build());
-      }
-      if (lazyFieldBuilder != null) {
-        itemBuilder.setMemberPattern(lazyFieldBuilder.build());
-      }
-      parent.accept(itemBuilder.build());
+      parent.accept(
+          KeepItemPattern.builder()
+              .setClassPattern(classDeclaration.getValue())
+              .setExtendsPattern(extendsDeclaration.getValue())
+              .setMemberPattern(memberDeclaration.getValue())
+              .build());
     }
   }
 
@@ -468,10 +723,59 @@
     }
   }
 
+  private static class OptionsDeclaration extends SingleDeclaration<KeepOptions> {
+
+    @Override
+    String kind() {
+      return "options";
+    }
+
+    @Override
+    KeepOptions getDefaultValue() {
+      return KeepOptions.keepAll();
+    }
+
+    @Override
+    KeepOptions parse(String name, Object value) {
+      return null;
+    }
+
+    @Override
+    AnnotationVisitor parseArray(String name, Consumer<KeepOptions> setValue) {
+      if (name.equals(KeepConstants.Target.disallow)) {
+        return new KeepOptionsVisitor(
+            options -> setValue.accept(KeepOptions.disallowBuilder().addAll(options).build()));
+      }
+      if (name.equals(KeepConstants.Target.allow)) {
+        return new KeepOptionsVisitor(
+            options -> setValue.accept(KeepOptions.allowBuilder().addAll(options).build()));
+      }
+      return null;
+    }
+  }
+
   private static class KeepTargetVisitor extends KeepItemVisitorBase {
 
-    public KeepTargetVisitor(Parent<KeepTarget> parent) {
-      super(item -> parent.accept(KeepTarget.builder().setItem(item).build()));
+    private final KeepTarget.Builder builder;
+    private final OptionsDeclaration optionsDeclaration = new OptionsDeclaration();
+
+    static KeepTargetVisitor create(Parent<KeepTarget> parent) {
+      KeepTarget.Builder builder = KeepTarget.builder();
+      return new KeepTargetVisitor(parent, builder);
+    }
+
+    private KeepTargetVisitor(Parent<KeepTarget> parent, KeepTarget.Builder builder) {
+      super(item -> parent.accept(builder.setItem(item).build()));
+      this.builder = builder;
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(String name) {
+      AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, builder::setOptions);
+      if (visitor != null) {
+        return visitor;
+      }
+      return super.visitArray(name);
     }
   }
 
@@ -481,4 +785,49 @@
       super(item -> parent.accept(KeepCondition.builder().setItem(item).build()));
     }
   }
+
+  private static class KeepOptionsVisitor extends AnnotationVisitorBase {
+
+    private final Parent<Collection<KeepOption>> parent;
+    private final Set<KeepOption> options = new HashSet<>();
+
+    public KeepOptionsVisitor(Parent<Collection<KeepOption>> parent) {
+      this.parent = parent;
+    }
+
+    @Override
+    public void visitEnum(String ignore, String descriptor, String value) {
+      if (!descriptor.equals(KeepConstants.Option.DESCRIPTOR)) {
+        super.visitEnum(ignore, descriptor, value);
+      }
+      KeepOption option;
+      switch (value) {
+        case Option.SHRINKING:
+          option = KeepOption.SHRINKING;
+          break;
+        case Option.OPTIMIZATION:
+          option = KeepOption.OPTIMIZING;
+          break;
+        case Option.OBFUSCATION:
+          option = KeepOption.OBFUSCATING;
+          break;
+        case Option.ACCESS_MODIFICATION:
+          option = KeepOption.ACCESS_MODIFICATION;
+          break;
+        case Option.ANNOTATION_REMOVAL:
+          option = KeepOption.ANNOTATION_REMOVAL;
+          break;
+        default:
+          super.visitEnum(ignore, descriptor, value);
+          return;
+      }
+      options.add(option);
+    }
+
+    @Override
+    public void visitEnd() {
+      parent.accept(options);
+      super.visitEnd();
+    }
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index e1d6d4e..dbe52b0 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -19,7 +19,10 @@
  * <p>TODO(b/248408342): Update the BNF and AST to be complete.
  *
  * <pre>
- *   EDGE ::= PRECONDITIONS -> CONSEQUENCES
+ *   EDGE ::= METAINFO PRECONDITIONS -> CONSEQUENCES
+ *   METAINFO = [CONTEXT] [DESCRIPTION]
+ *   CONTEXT = class-descriptor | method-descriptor | field-descriptor
+ *   DESCRIPTION = string-content
  *
  *   PRECONDITIONS ::= always | CONDITION+
  *   CONDITION ::= ITEM_PATTERN
@@ -27,7 +30,7 @@
  *   CONSEQUENCES ::= TARGET+
  *   TARGET ::= OPTIONS ITEM_PATTERN
  *   OPTIONS ::= keep-all | OPTION+
- *   OPTION ::= shrinking | optimizing | obfuscating | access-modifying
+ *   OPTION ::= shrinking | optimizing | obfuscating | access-modification | annotation-removal
  *
  *   ITEM_PATTERN
  *     ::= any
@@ -61,6 +64,7 @@
 public final class KeepEdge {
 
   public static class Builder {
+    private KeepEdgeMetaInfo metaInfo = KeepEdgeMetaInfo.none();
     private KeepPreconditions preconditions = KeepPreconditions.always();
     private KeepConsequences consequences;
 
@@ -76,11 +80,16 @@
       return this;
     }
 
+    public Builder setMetaInfo(KeepEdgeMetaInfo metaInfo) {
+      this.metaInfo = metaInfo;
+      return this;
+    }
+
     public KeepEdge build() {
       if (consequences.isEmpty()) {
         throw new KeepEdgeException("KeepEdge must have non-empty set of consequences.");
       }
-      return new KeepEdge(preconditions, consequences);
+      return new KeepEdge(preconditions, consequences, metaInfo);
     }
   }
 
@@ -88,14 +97,22 @@
     return new Builder();
   }
 
+  private final KeepEdgeMetaInfo metaInfo;
   private final KeepPreconditions preconditions;
   private final KeepConsequences consequences;
 
-  private KeepEdge(KeepPreconditions preconditions, KeepConsequences consequences) {
+  private KeepEdge(
+      KeepPreconditions preconditions, KeepConsequences consequences, KeepEdgeMetaInfo metaInfo) {
     assert preconditions != null;
     assert consequences != null;
+    assert metaInfo != null;
     this.preconditions = preconditions;
     this.consequences = consequences;
+    this.metaInfo = metaInfo;
+  }
+
+  public KeepEdgeMetaInfo getMetaInfo() {
+    return metaInfo;
   }
 
   public KeepPreconditions getPreconditions() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
new file mode 100644
index 0000000..662ad4c
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
@@ -0,0 +1,247 @@
+// 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.keepanno.ast;
+
+import java.util.Objects;
+
+public class KeepEdgeMetaInfo {
+
+  private static final KeepEdgeMetaInfo NONE =
+      new KeepEdgeMetaInfo(KeepEdgeContext.none(), KeepEdgeDescription.empty());
+
+  public static KeepEdgeMetaInfo none() {
+    return NONE;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  private final KeepEdgeContext context;
+  private final KeepEdgeDescription description;
+
+  private KeepEdgeMetaInfo(KeepEdgeContext context, KeepEdgeDescription description) {
+    this.context = context;
+    this.description = description;
+  }
+
+  public boolean hasDescription() {
+    return !KeepEdgeDescription.empty().equals(description);
+  }
+
+  public String getDescriptionString() {
+    return description.description;
+  }
+
+  public String getContextDescriptorString() {
+    return context.getDescriptorString();
+  }
+
+  public boolean hasContext() {
+    return !KeepEdgeContext.none().equals(context);
+  }
+
+  public static class Builder {
+    private KeepEdgeContext context = KeepEdgeContext.none();
+    private KeepEdgeDescription description = KeepEdgeDescription.empty();
+
+    public Builder setDescription(String description) {
+      this.description = new KeepEdgeDescription(description);
+      return this;
+    }
+
+    public Builder setContextFromClassDescriptor(String classDescriptor) {
+      context = new KeepEdgeClassContext(classDescriptor);
+      return this;
+    }
+
+    public Builder setContextFromMethodDescriptor(
+        String classDescriptor, String methodName, String methodDescriptor) {
+      context = new KeepEdgeMethodContext(classDescriptor, methodName, methodDescriptor);
+      return this;
+    }
+
+    public Builder setContextFromFieldDescriptor(
+        String classDescriptor, String fieldName, String fieldType) {
+      context = new KeepEdgeFieldContext(classDescriptor, fieldName, fieldType);
+      return this;
+    }
+
+    public KeepEdgeMetaInfo build() {
+      if (context.equals(KeepEdgeContext.none())
+          && description.equals(KeepEdgeDescription.empty())) {
+        return none();
+      }
+      return new KeepEdgeMetaInfo(context, description);
+    }
+  }
+
+  private static class KeepEdgeContext {
+    private static final KeepEdgeContext NONE = new KeepEdgeContext();
+
+    public static KeepEdgeContext none() {
+      return NONE;
+    }
+
+    private KeepEdgeContext() {}
+
+    public String getDescriptorString() {
+      throw new KeepEdgeException("Invalid attempt to get descriptor string from none context");
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+  }
+
+  private static class KeepEdgeClassContext extends KeepEdgeContext {
+    private final String classDescriptor;
+
+    public KeepEdgeClassContext(String classDescriptor) {
+      assert classDescriptor != null;
+      this.classDescriptor = classDescriptor;
+    }
+
+    @Override
+    public String getDescriptorString() {
+      return classDescriptor;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      KeepEdgeClassContext that = (KeepEdgeClassContext) o;
+      return classDescriptor.equals(that.classDescriptor);
+    }
+
+    @Override
+    public int hashCode() {
+      return classDescriptor.hashCode();
+    }
+  }
+
+  private static class KeepEdgeMethodContext extends KeepEdgeContext {
+    private final String classDescriptor;
+    private final String methodName;
+    private final String methodDescriptor;
+
+    public KeepEdgeMethodContext(
+        String classDescriptor, String methodName, String methodDescriptor) {
+      assert classDescriptor != null;
+      assert methodName != null;
+      assert methodDescriptor != null;
+      this.classDescriptor = classDescriptor;
+      this.methodName = methodName;
+      this.methodDescriptor = methodDescriptor;
+    }
+
+    @Override
+    public String getDescriptorString() {
+      return classDescriptor + methodName + methodDescriptor;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      KeepEdgeMethodContext that = (KeepEdgeMethodContext) o;
+      return classDescriptor.equals(that.classDescriptor)
+          && methodName.equals(that.methodName)
+          && methodDescriptor.equals(that.methodDescriptor);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(classDescriptor, methodName, methodDescriptor);
+    }
+  }
+
+  private static class KeepEdgeFieldContext extends KeepEdgeContext {
+    private final String classDescriptor;
+    private final String fieldName;
+    private final String fieldType;
+
+    public KeepEdgeFieldContext(String classDescriptor, String fieldName, String fieldType) {
+      this.classDescriptor = classDescriptor;
+      this.fieldName = fieldName;
+      this.fieldType = fieldType;
+    }
+
+    @Override
+    public String getDescriptorString() {
+      return classDescriptor + fieldName + ":" + fieldType;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      KeepEdgeFieldContext that = (KeepEdgeFieldContext) o;
+      return classDescriptor.equals(that.classDescriptor)
+          && fieldName.equals(that.fieldName)
+          && fieldType.equals(that.fieldType);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(classDescriptor, fieldName, fieldType);
+    }
+  }
+
+  private static class KeepEdgeDescription {
+    private static final KeepEdgeDescription EMPTY = new KeepEdgeDescription("");
+
+    public static KeepEdgeDescription empty() {
+      return EMPTY;
+    }
+
+    private final String description;
+
+    public KeepEdgeDescription(String description) {
+      assert description != null;
+      this.description = description;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      KeepEdgeDescription that = (KeepEdgeDescription) o;
+      return description.equals(that.description);
+    }
+
+    @Override
+    public int hashCode() {
+      return description.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return description;
+    }
+  }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
index 02c8768..ea30422 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
@@ -7,57 +7,34 @@
 public abstract class KeepExtendsPattern {
 
   public static KeepExtendsPattern any() {
-    return Any.getInstance();
+    return Some.getAnyInstance();
   }
 
   public static class Builder {
 
-    private KeepExtendsPattern pattern;
+    private KeepExtendsPattern pattern = KeepExtendsPattern.any();
 
     private Builder() {}
 
-    public Builder any() {
-      pattern = Any.getInstance();
-      return this;
-    }
-
     public Builder classPattern(KeepQualifiedClassNamePattern pattern) {
       this.pattern = new Some(pattern);
       return this;
     }
-  }
 
-  private static class Any extends KeepExtendsPattern {
-
-    private static final Any INSTANCE = new Any();
-
-    public static Any getInstance() {
-      return INSTANCE;
-    }
-
-    @Override
-    public boolean isAny() {
-      return true;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      return this == obj;
-    }
-
-    @Override
-    public int hashCode() {
-      return System.identityHashCode(this);
-    }
-
-    @Override
-    public String toString() {
-      return "*";
+    public KeepExtendsPattern build() {
+      return pattern;
     }
   }
 
   private static class Some extends KeepExtendsPattern {
 
+    private static final KeepExtendsPattern ANY_INSTANCE =
+        new Some(KeepQualifiedClassNamePattern.any());
+
+    private static KeepExtendsPattern getAnyInstance() {
+      return ANY_INSTANCE;
+    }
+
     private final KeepQualifiedClassNamePattern pattern;
 
     public Some(KeepQualifiedClassNamePattern pattern) {
@@ -71,6 +48,11 @@
     }
 
     @Override
+    public KeepQualifiedClassNamePattern asClassNamePattern() {
+      return pattern;
+    }
+
+    @Override
     public boolean equals(Object o) {
       if (this == o) {
         return true;
@@ -100,4 +82,6 @@
   private KeepExtendsPattern() {}
 
   public abstract boolean isAny();
+
+  public abstract KeepQualifiedClassNamePattern asClassNamePattern();
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
index 50d3921..7f84090 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
@@ -29,7 +29,7 @@
 
   public static class Builder {
 
-    private KeepQualifiedClassNamePattern classNamePattern;
+    private KeepQualifiedClassNamePattern classNamePattern = KeepQualifiedClassNamePattern.any();
     private KeepExtendsPattern extendsPattern = KeepExtendsPattern.any();
     private KeepMemberPattern memberPattern = KeepMemberPattern.none();
 
@@ -58,9 +58,6 @@
     }
 
     public KeepItemPattern build() {
-      if (classNamePattern == null) {
-        throw new KeepEdgeException("Class pattern must define a class name pattern.");
-      }
       return new KeepItemPattern(classNamePattern, extendsPattern, memberPattern);
     }
   }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java
index 8e1b2e9..ba647eb 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java
@@ -22,7 +22,8 @@
     SHRINKING,
     OPTIMIZING,
     OBFUSCATING,
-    ACCESS_MODIFYING,
+    ACCESS_MODIFICATION,
+    ANNOTATION_REMOVAL,
   }
 
   public static KeepOptions keepAll() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index 286c07a..f5739e1 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -6,6 +6,8 @@
 import com.android.tools.r8.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo;
+import com.android.tools.r8.keepanno.ast.KeepExtendsPattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldAccessPattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
 import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
@@ -40,7 +42,7 @@
 
   public void extract(KeepEdge edge) {
     List<ItemRule> consequentRules = getConsequentRules(edge.getConsequences());
-    printConditionalRules(consequentRules, edge.getPreconditions());
+    printConditionalRules(consequentRules, edge.getPreconditions(), edge.getMetaInfo());
   }
 
   private List<ItemRule> getConsequentRules(KeepConsequences consequences) {
@@ -49,8 +51,49 @@
     return consequentItems;
   }
 
+  private void printHeader(StringBuilder builder, KeepEdgeMetaInfo metaInfo) {
+    if (metaInfo.hasContext()) {
+      builder.append("# context: ").append(metaInfo.getContextDescriptorString()).append('\n');
+    }
+    if (metaInfo.hasDescription()) {
+      String escapedDescription = escapeLineBreaks(metaInfo.getDescriptionString());
+      builder.append("# description: ").append(escapedDescription).append('\n');
+    }
+  }
+
+  private String escapeChar(char c) {
+    if (c == '\n') {
+      return "\\n";
+    }
+    if (c == '\r') {
+      return "\\r";
+    }
+    return null;
+  }
+
+  private String escapeLineBreaks(String string) {
+    char[] charArray = string.toCharArray();
+    for (int i = 0; i < charArray.length; i++) {
+      // We don't expect escape chars, so wait with constructing a new string until found.
+      if (escapeChar(charArray[i]) != null) {
+        StringBuilder builder = new StringBuilder(string.substring(0, i));
+        for (int j = i; j < charArray.length; j++) {
+          char c = charArray[j];
+          String escaped = escapeChar(c);
+          if (escaped != null) {
+            builder.append(escaped);
+          } else {
+            builder.append(c);
+          }
+        }
+        return builder.toString();
+      }
+    }
+    return string;
+  }
+
   private void printConditionalRules(
-      List<ItemRule> consequentRules, KeepPreconditions preconditions) {
+      List<ItemRule> consequentRules, KeepPreconditions preconditions, KeepEdgeMetaInfo metaInfo) {
     boolean[] hasAtLeastOneConditionalClause = new boolean[1];
     preconditions.forEach(
         condition -> {
@@ -66,6 +109,7 @@
                 // disjunctions so conservatively we keep the consequences if any one of
                 // the preconditions hold.
                 StringBuilder builder = new StringBuilder();
+                printHeader(builder, metaInfo);
                 if (!consequentItem.isMemberOnlyConsequent()
                     || !conditionItem
                         .getClassNamePattern()
@@ -82,7 +126,11 @@
     if (!hasAtLeastOneConditionalClause[0]) {
       // If there are no preconditions, print each consequent as is.
       consequentRules.forEach(
-          r -> ruleConsumer.accept(printConsequentRule(new StringBuilder(), r).toString()));
+          r -> {
+            StringBuilder builder = new StringBuilder();
+            printHeader(builder, metaInfo);
+            ruleConsumer.accept(printConsequentRule(builder, r).toString());
+          });
     }
   }
 
@@ -103,8 +151,10 @@
   private static StringBuilder printItem(StringBuilder builder, KeepItemPattern clazzPattern) {
     builder.append("class ");
     printClassName(builder, clazzPattern.getClassNamePattern());
-    if (!clazzPattern.getExtendsPattern().isAny()) {
-      throw new Unimplemented();
+    KeepExtendsPattern extendsPattern = clazzPattern.getExtendsPattern();
+    if (!extendsPattern.isAny()) {
+      builder.append(" extends ");
+      printClassName(builder, extendsPattern.asClassNamePattern());
     }
     KeepMemberPattern member = clazzPattern.getMemberPattern();
     if (member.isNone()) {
@@ -247,8 +297,10 @@
         return "optimization";
       case OBFUSCATING:
         return "obfuscation";
-      case ACCESS_MODIFYING:
+      case ACCESS_MODIFICATION:
         return "accessmodification";
+      case ANNOTATION_REMOVAL:
+        return "annotationremoval";
       default:
         throw new Unimplemented();
     }
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index f578416..aea1c45 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.naming.PrefixRewritingNamingLens;
 import com.android.tools.r8.naming.RecordRewritingNamingLens;
+import com.android.tools.r8.naming.VarHandleDesugaringRewritingNamingLens;
 import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.origin.Origin;
@@ -293,6 +294,13 @@
               appView.setNamingLens(
                   RecordRewritingNamingLens.createRecordRewritingNamingLens(appView)));
 
+      timing.time(
+          "Create MethodHandle.Lookup rewriting lens",
+          () ->
+              appView.setNamingLens(
+                  VarHandleDesugaringRewritingNamingLens
+                      .createVarHandleDesugaringRewritingNamingLens(appView)));
+
       if (options.isGeneratingDex()
           && hasDexResources
           && hasClassResources
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5a791ab..3102f0d 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -7,8 +7,6 @@
 import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
 
 import com.android.tools.r8.androidapi.ApiReferenceStubber;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryKeepRuleGenerator;
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.ApplicationWriter;
@@ -20,12 +18,9 @@
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.AppliedGraphLens;
-import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexDebugEvent;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -36,6 +31,7 @@
 import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
@@ -947,10 +943,8 @@
                         if (code == null) {
                           return;
                         }
-                        if (code.isCfCode()) {
-                          assert verifyOriginalMethodInPosition(code.asCfCode(), originalMethod);
-                        } else if (code.isDexCode()) {
-                          assert verifyOriginalMethodInDebugInfo(code.asDexCode(), originalMethod);
+                        if (code.isCfCode() || code.isDexCode()) {
+                          assert verifyOriginalMethodInPosition(code, originalMethod, method);
                         } else {
                           assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
                         }
@@ -959,26 +953,25 @@
     return true;
   }
 
-  private static boolean verifyOriginalMethodInPosition(CfCode code, DexMethod originalMethod) {
-    for (CfInstruction instruction : code.getInstructions()) {
-      if (!instruction.isPosition()) {
-        continue;
-      }
-      CfPosition position = instruction.asPosition();
-      assert position.getPosition().getOutermostCaller().getMethod() == originalMethod;
-    }
-    return true;
-  }
-
-  private static boolean verifyOriginalMethodInDebugInfo(DexCode code, DexMethod originalMethod) {
-    if (code.getDebugInfo() == null || code.getDebugInfo().isPcBasedInfo()) {
-      return true;
-    }
-    for (DexDebugEvent event : code.getDebugInfo().asEventBasedInfo().events) {
-      assert !event.isPositionFrame()
-          || event.asSetPositionFrame().getPosition().getOutermostCaller().getMethod()
-              == originalMethod;
-    }
+  private static boolean verifyOriginalMethodInPosition(
+      Code code, DexMethod originalMethod, ProgramMethod context) {
+    code.forEachPosition(
+        position -> {
+          if (position.isOutlineCaller()) {
+            // Check the outlined positions for the original method
+            position
+                .getOutlinePositions()
+                .forEach(
+                    (ignored, outlinePosition) -> {
+                      assert outlinePosition.hasMethodInChain(originalMethod);
+                    });
+          } else if (context.getDefinition().isD8R8Synthesized()) {
+            // TODO(b/261971803): Enable assert.
+            assert true || position.hasMethodInChain(originalMethod);
+          } else {
+            assert position.getOutermostCaller().getMethod() == originalMethod;
+          }
+        });
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index df6ee75..1fd5ce7 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -42,6 +42,8 @@
   public static final CfVersion V18_PREVIEW = new CfVersion(Opcodes.V18 | Opcodes.V_PREVIEW);
   public static final CfVersion V19 = new CfVersion(Opcodes.V19);
   public static final CfVersion V19_PREVIEW = new CfVersion(Opcodes.V19 | Opcodes.V_PREVIEW);
+  public static final CfVersion V20 = new CfVersion(Opcodes.V20);
+  public static final CfVersion V20_PREVIEW = new CfVersion(Opcodes.V20 | Opcodes.V_PREVIEW);
 
   private final int version;
 
@@ -64,7 +66,8 @@
     CfVersion.V16,
     CfVersion.V17,
     CfVersion.V18,
-    CfVersion.V19
+    CfVersion.V19,
+    CfVersion.V20
   };
 
   // Private constructor in case we want to canonicalize versions.
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 84334f5..c4e0493 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -223,8 +223,13 @@
             type = Type.VIRTUAL;
             canonicalMethod = method;
           } else {
-            type = Type.POLYMORPHIC;
-            callSiteProto = method.proto;
+            if (builder.appView.options().shouldDesugarVarHandle()) {
+              type = Type.VIRTUAL;
+              canonicalMethod = method;
+            } else {
+              type = Type.POLYMORPHIC;
+              callSiteProto = method.proto;
+            }
           }
           break;
         }
diff --git a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
index cec1de3..34898725 100644
--- a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
+++ b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
@@ -5,8 +5,6 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableMap;
-import java.util.Map;
 
 public abstract class ApplicationReaderMap {
 
@@ -23,9 +21,6 @@
     if (options.shouldDesugarRecords() && !options.testing.disableRecordApplicationReaderMap) {
       result = new RecordMap(options.dexItemFactory());
     }
-    if (options.shouldDesugarVarHandle()) {
-      return new VarHandleMap(result);
-    }
     return result;
   }
 
@@ -72,34 +67,4 @@
       return type == factory.recordType ? factory.recordTagType : type;
     }
   }
-
-  public static class VarHandleMap extends ApplicationReaderMap {
-
-    private final ApplicationReaderMap previous;
-    private final Map<String, String> descriptorMap =
-        ImmutableMap.of(
-            DexItemFactory.varHandleDescriptorString,
-            DexItemFactory.desugarVarHandleDescriptorString,
-            DexItemFactory.methodHandlesLookupDescriptorString,
-            DexItemFactory.desugarMethodHandlesLookupDescriptorString);
-
-    public VarHandleMap(ApplicationReaderMap previous) {
-      this.previous = previous;
-    }
-
-    @Override
-    public String getDescriptor(String descriptor) {
-      return previous.getDescriptor(descriptorMap.getOrDefault(descriptor, descriptor));
-    }
-
-    @Override
-    public DexType getType(DexType type) {
-      return type;
-    }
-
-    @Override
-    public DexType getInvertedType(DexType type) {
-      return type;
-    }
-  }
 }
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 1d05d43..f360949 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -62,7 +62,7 @@
       // Record original method signatures.
       for (DexEncodedMethod encodedMethod : clazz.methods()) {
         DexMethod method = encodedMethod.getReference();
-        DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
+        DexMethod original = appView.graphLens().getOriginalMethodSignatureForMapping(method);
         DexMethod existing = originalMethodSignatures.inverse().get(original);
         if (existing == null) {
           originalMethodSignatures.put(method, original);
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 169a880..fff7a6e 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -59,6 +59,7 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Set;
+import java.util.function.Consumer;
 import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 
@@ -972,4 +973,13 @@
             .build();
     return helper.run();
   }
+
+  @Override
+  public void forEachPosition(Consumer<Position> positionConsumer) {
+    for (CfInstruction instruction : getInstructions()) {
+      if (instruction.isPosition()) {
+        positionConsumer.accept(instruction.asPosition().getPosition());
+      }
+    }
+  }
 }
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 0475e2c..bc0c72b 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.RetracerForCodePrinting;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import java.util.function.Consumer;
 
 public abstract class Code extends CachedHashValueDexItem {
 
@@ -190,7 +191,7 @@
       Position callerPosition, Position oldPosition, boolean isCalleeD8R8Synthesized) {
     Position outermostCaller = oldPosition.getOutermostCaller();
     if (!isCalleeD8R8Synthesized) {
-      return oldPosition.withOutermostCallerPosition(callerPosition);
+      return removeSameMethodAndLineZero(oldPosition, callerPosition);
     }
     // We can replace the position since the callee was synthesized by the compiler, however, if
     // the position carries special information we need to copy it.
@@ -208,4 +209,23 @@
     }
     return oldPosition.replacePosition(outermostCaller, positionBuilder.build());
   }
+
+  @Deprecated()
+  // TODO(b/261971803): When having complete control over the positions we should not need this.
+  private static Position removeSameMethodAndLineZero(
+      Position calleePosition, Position callerPosition) {
+    Position outermostCaller = calleePosition.getOutermostCaller();
+    if (outermostCaller.getLine() == 0) {
+      while (callerPosition != null
+          && outermostCaller.getMethod() == callerPosition.getMethod()
+          && callerPosition.getLine() == 0) {
+        callerPosition = callerPosition.getCallerPosition();
+      }
+    }
+    return calleePosition.withOutermostCallerPosition(callerPosition);
+  }
+
+  public void forEachPosition(Consumer<Position> positionConsumer) {
+    // Intentionally empty. Override where we have fully build CF or DEX code.
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
index b64ef99..f949455 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
@@ -44,6 +44,14 @@
     return recordWitnesses;
   }
 
+  public boolean hasReadMethodHandlesLookupReferenceFromProgramClass() {
+    return !methodHandlesLookupWitnesses.isEmpty();
+  }
+
+  public Set<DexType> getMethodHandlesLookupWitnesses() {
+    return methodHandlesLookupWitnesses;
+  }
+
   public boolean hasReadVarHandleReferenceFromProgramClass() {
     return !varHandleWitnesses.isEmpty();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 8ca811b..60ce8b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -52,6 +52,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 // DexCode corresponds to code item in dalvik/dex-format.html
 public class DexCode extends Code implements DexWritableCode, StructuralItem<DexCode> {
@@ -775,6 +776,18 @@
     }
   }
 
+  @Override
+  public void forEachPosition(Consumer<Position> positionConsumer) {
+    if (getDebugInfo() == null || getDebugInfo().isPcBasedInfo()) {
+      return;
+    }
+    for (DexDebugEvent event : getDebugInfo().asEventBasedInfo().events) {
+      if (event.isPositionFrame()) {
+        positionConsumer.accept(event.asSetPositionFrame().getPosition());
+      }
+    }
+  }
+
   public static class Try extends DexItem implements StructuralItem<Try> {
 
     public static final Try[] EMPTY_ARRAY = new Try[0];
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 318b353..761298d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1093,12 +1093,39 @@
 
   public DexEncodedMethod toTypeSubstitutedMethod(DexMethod method) {
     checkIfObsolete();
-    return toTypeSubstitutedMethod(method, null);
+    return toTypeSubstitutedMethodHelper(method, isD8R8Synthesized(), null);
   }
 
   public DexEncodedMethod toTypeSubstitutedMethod(DexMethod method, Consumer<Builder> consumer) {
     checkIfObsolete();
-    Builder builder = builder(this);
+    return toTypeSubstitutedMethodHelper(method, isD8R8Synthesized(), consumer);
+  }
+
+  public DexEncodedMethod toTypeSubstitutedMethodAsInlining(
+      DexMethod method, DexItemFactory factory) {
+    checkIfObsolete();
+    return toTypeSubstitutedMethodAsInlining(method, factory, null);
+  }
+
+  public DexEncodedMethod toTypeSubstitutedMethodAsInlining(
+      DexMethod method, DexItemFactory factory, Consumer<Builder> consumer) {
+    return toTypeSubstitutedMethodHelper(
+        method,
+        true,
+        builder -> {
+          if (code != null) {
+            builder.setCode(getCode().getCodeAsInlining(method, this, factory));
+          }
+          if (consumer != null) {
+            consumer.accept(builder);
+          }
+        });
+  }
+
+  private DexEncodedMethod toTypeSubstitutedMethodHelper(
+      DexMethod method, boolean isD8R8Synthesized, Consumer<Builder> consumer) {
+    checkIfObsolete();
+    Builder builder = isD8R8Synthesized ? syntheticBuilder(this) : builder(this);
     if (isNonPrivateVirtualMethod() && isLibraryMethodOverride() != OptionalBool.unknown()) {
       builder.setIsLibraryMethodOverride(isLibraryMethodOverride());
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 94b8840..57d6d34 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -80,8 +80,6 @@
   public static final String varHandleDescriptorString = "Ljava/lang/invoke/VarHandle;";
   public static final String desugarMethodHandlesLookupDescriptorString =
       "Lcom/android/tools/r8/DesugarMethodHandlesLookup;";
-  public static final String methodHandlesLookupDescriptorString =
-      "Ljava/lang/invoke/MethodHandles$Lookup;";
   public static final String dalvikAnnotationOptimizationPrefixString =
       "Ldalvik/annotation/optimization/";
 
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 46d91ef..43558a8 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -329,6 +329,18 @@
     return original;
   }
 
+  public final DexMethod getOriginalMethodSignatureForMapping(DexMethod method) {
+    GraphLens current = this;
+    DexMethod original = method;
+    while (current.isNonIdentityLens()) {
+      NonIdentityGraphLens nonIdentityLens = current.asNonIdentityLens();
+      original = nonIdentityLens.getPreviousMethodSignatureForMapping(original);
+      current = nonIdentityLens.getPrevious();
+    }
+    assert current.isIdentityLens();
+    return original;
+  }
+
   public final DexField getRenamedFieldSignature(DexField originalField) {
     return getRenamedFieldSignature(originalField, null);
   }
@@ -971,6 +983,15 @@
 
     public abstract DexMethod getPreviousMethodSignature(DexMethod method);
 
+    /***
+     * The previous mapping for a method often coincides with the previous method signature, but it
+     * may not, for example for bridges inserted in vertically merged classes where the original
+     * signature is used for computing invoke-super but should not be used for mapping output.
+     */
+    public DexMethod getPreviousMethodSignatureForMapping(DexMethod method) {
+      return getPreviousMethodSignature(method);
+    }
+
     public abstract DexMethod getNextMethodSignature(DexMethod method);
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 80bb77b..18624c5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -86,6 +86,7 @@
           return Type.STATIC;
         case Opcodes.INVOKEVIRTUAL:
           return appView.dexItemFactory().polymorphicMethods.isPolymorphicInvoke(invokedMethod)
+                  && !appView.options().shouldDesugarVarHandle()
               ? Type.POLYMORPHIC
               : Type.VIRTUAL;
         default:
@@ -94,11 +95,6 @@
     }
 
     public static Type fromInvokeSpecial(
-        DexMethod invokedMethod, DexClassAndMethod context, AppView<?> appView) {
-      return fromInvokeSpecial(invokedMethod, context, appView, appView.codeLens());
-    }
-
-    public static Type fromInvokeSpecial(
         DexMethod invokedMethod,
         DexClassAndMethod context,
         AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 5e88574..4c809dc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -33,13 +33,19 @@
   protected final Position callerPosition;
 
   private final boolean removeInnerFramesIfThrowingNpe;
+  private final boolean isD8R8Synthesized;
 
   private Position(
-      int line, DexMethod method, Position callerPosition, boolean removeInnerFramesIfThrowingNpe) {
+      int line,
+      DexMethod method,
+      Position callerPosition,
+      boolean removeInnerFramesIfThrowingNpe,
+      boolean isD8R8Synthesized) {
     this.line = line;
     this.method = method;
     this.callerPosition = callerPosition;
     this.removeInnerFramesIfThrowingNpe = removeInnerFramesIfThrowingNpe;
+    this.isD8R8Synthesized = isD8R8Synthesized;
   }
 
   public boolean isSyntheticPosition() {
@@ -50,10 +56,18 @@
     return removeInnerFramesIfThrowingNpe;
   }
 
+  public boolean isD8R8Synthesized() {
+    return isD8R8Synthesized;
+  }
+
   public boolean isOutline() {
     return false;
   }
 
+  public boolean isOutlineCaller() {
+    return false;
+  }
+
   public DexMethod getOutlineCallee() {
     return null;
   }
@@ -106,7 +120,8 @@
         .withInt(Position::getLine)
         .withNullableItem(Position::getMethod)
         .withNullableItem(Position::getCallerPosition)
-        .withBool(Position::isRemoveInnerFramesIfThrowingNpe);
+        .withBool(Position::isRemoveInnerFramesIfThrowingNpe)
+        .withBool(Position::isD8R8Synthesized);
   }
 
   public static Position syntheticNone() {
@@ -120,8 +135,17 @@
       assert position.isNone();
       position = SourcePosition.builder().setMethod(context.getReference()).build();
     }
-    assert position.getOutermostCaller().method
-        == appView.graphLens().getOriginalMethodSignature(context.getReference());
+    if (context.getDefinition().isD8R8Synthesized()) {
+      // Some rewritings map a synthetic method back to an original in the program. To ensure we
+      // have correct line information we have to rewrite the positions as inline position
+      // therefore we only check if the original method is present.
+      DexMethod originalMethodSignature =
+          appView.graphLens().getOriginalMethodSignature(context.getReference());
+      assert position.hasMethodInChain(originalMethodSignature);
+    } else {
+      assert position.getOutermostCaller().method
+          == appView.graphLens().getOriginalMethodSignature(context.getReference());
+    }
     return position;
   }
 
@@ -167,6 +191,21 @@
     return null;
   }
 
+  public boolean hasPositionMatching(Predicate<Position> positionPredicate) {
+    Position lastPosition = this;
+    while (lastPosition != null) {
+      if (positionPredicate.test(lastPosition)) {
+        return true;
+      }
+      lastPosition = lastPosition.getCallerPosition();
+    }
+    return false;
+  }
+
+  public boolean hasMethodInChain(DexMethod method) {
+    return hasPositionMatching(position -> position.getMethod() == method);
+  }
+
   public Position withOutermostCallerPosition(Position newOutermostCallerPosition) {
     return builderWithCopy()
         .setCallerPosition(
@@ -238,6 +277,7 @@
     protected DexMethod method;
     protected Position callerPosition;
     protected boolean removeInnerFramesIfThrowingNpe;
+    protected boolean isD8R8Synthesized;
 
     protected boolean noCheckOfPosition;
     protected boolean noCheckOfMethod;
@@ -268,6 +308,11 @@
       return self();
     }
 
+    public B setIsD8R8Synthesized(boolean isD8R8Synthesized) {
+      this.isD8R8Synthesized = isD8R8Synthesized;
+      return self();
+    }
+
     public B disableLineCheck() {
       noCheckOfPosition = true;
       return self();
@@ -286,7 +331,7 @@
     // A no-position marker. Not having a position means the position is implicitly defined by the
     // context, e.g., the marker does not materialize anything concrete.
     private static final SourcePosition NO_POSITION =
-        new SourcePosition(-1, null, null, false, null);
+        new SourcePosition(-1, null, null, false, false, null);
 
     public final DexString file;
 
@@ -299,8 +344,9 @@
         DexMethod method,
         Position callerPosition,
         boolean removeInnerFramesIfThrowingNpe,
+        boolean isD8R8Synthesized,
         DexString file) {
-      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
       this.file = file;
       assert callerPosition == null || callerPosition.method != null;
     }
@@ -327,7 +373,8 @@
           .setFile(file)
           .setMethod(method)
           .setCallerPosition(callerPosition)
-          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+          .setIsD8R8Synthesized(isD8R8Synthesized());
     }
 
     @Override
@@ -359,7 +406,7 @@
         assert noCheckOfPosition || line >= 0;
         assert noCheckOfMethod || method != null;
         return new SourcePosition(
-            line, method, callerPosition, removeInnerFramesIfThrowingNpe, file);
+            line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized, file);
       }
     }
   }
@@ -370,14 +417,15 @@
     // This is used specifically to mark exceptional exit blocks from synchronized methods in
     // release.
     private static final Position NO_POSITION_SYNTHETIC =
-        new SyntheticPosition(-1, null, null, false);
+        new SyntheticPosition(-1, null, null, false, false);
 
     private SyntheticPosition(
         int line,
         DexMethod method,
         Position callerPosition,
-        boolean removeInnerFramesIfThrowingNpe) {
-      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+        boolean removeInnerFramesIfThrowingNpe,
+        boolean isD8R8Synthesized) {
+      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
     }
 
     @Override
@@ -396,7 +444,8 @@
           .setLine(line)
           .setMethod(method)
           .setCallerPosition(callerPosition)
-          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+          .setIsD8R8Synthesized(isD8R8Synthesized());
     }
 
     @Override
@@ -422,7 +471,8 @@
       public SyntheticPosition build() {
         assert noCheckOfPosition || line >= 0;
         assert noCheckOfMethod || method != null;
-        return new SyntheticPosition(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+        return new SyntheticPosition(
+            line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
       }
     }
   }
@@ -433,8 +483,9 @@
         int line,
         DexMethod method,
         Position callerPosition,
-        boolean removeInnerFramesIfThrowingNpe) {
-      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+        boolean removeInnerFramesIfThrowingNpe,
+        boolean isD8R8Synthesized) {
+      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
     }
 
     @Override
@@ -453,7 +504,8 @@
           .setLine(line)
           .setMethod(method)
           .setCallerPosition(callerPosition)
-          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+          .setIsD8R8Synthesized(isD8R8Synthesized());
     }
 
     @Override
@@ -477,7 +529,8 @@
 
       @Override
       public OutlinePosition build() {
-        return new OutlinePosition(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+        return new OutlinePosition(
+            line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
       }
     }
   }
@@ -500,10 +553,11 @@
         DexMethod method,
         Position callerPosition,
         boolean removeInnerFramesIfThrowingNpe,
+        boolean isD8R8Synthesized,
         Int2StructuralItemArrayMap<Position> outlinePositions,
         DexMethod outlineCallee,
         boolean isOutline) {
-      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
       this.outlinePositions = outlinePositions;
       this.outlineCallee = outlineCallee;
       this.isOutline = isOutline;
@@ -528,7 +582,8 @@
               .setCallerPosition(callerPosition)
               .setOutlineCallee(outlineCallee)
               .setIsOutline(isOutline)
-              .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+              .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+              .setIsD8R8Synthesized(isD8R8Synthesized());
       outlinePositions.forEach(outlineCallerPositionBuilder::addOutlinePosition);
       return outlineCallerPositionBuilder;
     }
@@ -539,6 +594,11 @@
     }
 
     @Override
+    public boolean isOutlineCaller() {
+      return true;
+    }
+
+    @Override
     public DexMethod getOutlineCallee() {
       return outlineCallee;
     }
@@ -600,6 +660,7 @@
             method,
             callerPosition,
             removeInnerFramesIfThrowingNpe,
+            isD8R8Synthesized,
             outlinePositionsBuilder.build(),
             outlineCallee,
             isOutline);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index ea0f655..3242779 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -258,10 +258,11 @@
   }
 
   private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) {
-    // If this instruction has already been inlined then this.method must be the outermost caller.
+    // If this instruction has already been inlined then the original method must be in the caller
+    // chain.
     Position position = entry.getPosition();
-    assert !position.hasCallerPosition()
-        || position.getOutermostCaller().getMethod() == originalMethod;
+    // TODO(b/261971803): The original method should probably always be in the chain.
+    assert !position.hasCallerPosition() || position.hasMethodInChain(originalMethod);
     return canonicalPositions.getCanonical(
         position
             .builderWithCopy()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 46266fc..c621494 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -663,7 +663,11 @@
                             .setGenericSignature(encodedMethod.getGenericSignature())
                             .setAnnotations(encodedMethod.annotations())
                             .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
-                            .setCode(encodedMethod.getCode())
+                            .setCode(
+                                encodedMethod
+                                    .getCode()
+                                    .getCodeAsInlining(
+                                        callTarget, encodedMethod, appView.dexItemFactory()))
                             .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
                             .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
@@ -749,7 +753,11 @@
                             .setGenericSignature(encodedMethod.getGenericSignature())
                             .setAnnotations(encodedMethod.annotations())
                             .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
-                            .setCode(encodedMethod.getCode())
+                            .setCode(
+                                encodedMethod
+                                    .getCode()
+                                    .getCodeAsInlining(
+                                        callTarget, encodedMethod, appView.dexItemFactory()))
                             .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
                             .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
index ae232bd..380d5d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
@@ -39,6 +39,10 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
 import org.objectweb.asm.Opcodes;
 
 public class VarHandleDesugaring implements CfInstructionDesugaring, CfClassSynthesizerDesugaring {
@@ -52,7 +56,6 @@
 
   public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
     VarHandleDesugaringMethods.registerSynthesizedCodeReferences(factory);
-    factory.createSynthesizedType(DexItemFactory.desugarMethodHandlesLookupDescriptorString);
   }
 
   public VarHandleDesugaring(AppView<?> appView) {
@@ -63,7 +66,7 @@
   @Override
   public void scan(
       ProgramMethod programMethod, CfInstructionDesugaringEventConsumer eventConsumer) {
-    if (programMethod.getHolderType() == factory.desugarVarHandleType) {
+    if (programMethod.getHolderType() == factory.varHandleType) {
       return;
     }
     CfCode cfCode = programMethod.getDefinition().getCode().asCfCode();
@@ -90,12 +93,12 @@
   }
 
   private static boolean refersToVarHandle(DexType type, DexItemFactory factory) {
-    if (type == factory.varHandleType) {
-      // All references to java.lang.invoke.VarHandle is rewritten during application reading.
+    if (type == factory.desugarVarHandleType) {
+      // All references to java.lang.invoke.VarHandle is rewritten during application writing.
       assert false;
       return true;
     }
-    return type == factory.desugarVarHandleType;
+    return type == factory.varHandleType;
   }
 
   private static boolean refersToVarHandle(DexType[] types, DexItemFactory factory) {
@@ -130,13 +133,13 @@
   }
 
   private static boolean refersToMethodHandlesLookup(DexType type, DexItemFactory factory) {
-    if (type == factory.methodHandlesLookupType) {
+    if (type == factory.desugarMethodHandlesLookupType) {
       // All references to java.lang.invoke.MethodHandles$Lookup is rewritten during application
-      // reading.
+      // writing.
       assert false;
       return true;
     }
-    return type == factory.desugarMethodHandlesLookupType;
+    return type == factory.methodHandlesLookupType;
   }
 
   private static boolean refersToMethodHandlesLookup(DexType[] types, DexItemFactory factory) {
@@ -164,7 +167,6 @@
 
   public static boolean refersToMethodHandlesLookup(DexField field, DexItemFactory factory) {
     if (refersToMethodHandlesLookup(field.holder, factory)) {
-      assert false : "The MethodHandles$Lookup class has no fields.";
       return true;
     }
     return refersToMethodHandlesLookup(field.type, factory);
@@ -177,7 +179,7 @@
         .ensureGlobalClass(
             () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
             kinds -> kinds.METHOD_HANDLES_LOOKUP,
-            factory.desugarMethodHandlesLookupType,
+            factory.lookupType,
             contexts,
             appView,
             builder ->
@@ -198,7 +200,7 @@
         .ensureGlobalClass(
             () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
             kinds -> kinds.VAR_HANDLE,
-            factory.desugarVarHandleType,
+            factory.varHandleType,
             contexts,
             appView,
             builder ->
@@ -245,13 +247,13 @@
     DexType holder = invoke.getMethod().getHolderType();
     if (holder != factory.methodHandlesType
         && holder != factory.methodHandlesLookupType
-        && holder != factory.desugarVarHandleType) {
+        && holder != factory.varHandleType) {
       return DesugarDescription.nothing();
     }
     DexMethod method = invoke.getMethod();
     if (method.getHolderType() == factory.methodHandlesType) {
       if (method.getName().equals(factory.createString("lookup"))
-          && method.getReturnType() == factory.desugarMethodHandlesLookupType
+          && method.getReturnType() == factory.lookupType
           && method.getArity() == 0
           && invoke.isInvokeStatic()) {
         return computeMethodHandlesLookup(factory);
@@ -260,18 +262,7 @@
       }
     }
 
-    if (method.getHolderType() == factory.methodHandlesLookupType) {
-      assert invoke.isInvokeVirtual();
-
-      if (invoke.getMethod().getReturnType().equals(factory.desugarVarHandleType)) {
-        return computeInvokeMethodHandleLookupMethodReturningVarHandle(factory, invoke);
-      } else {
-        assert invoke.getMethod().getReturnType().equals(factory.methodHandleType);
-        return computeInvokeMethodHandleLookupMethodReturningMethodHandle(factory, invoke);
-      }
-    }
-
-    if (method.getHolderType() == factory.desugarVarHandleType) {
+    if (method.getHolderType() == factory.varHandleType) {
       assert invoke.isInvokeVirtual();
       DexString name = method.getName();
       int arity = method.getProto().getArity();
@@ -304,62 +295,18 @@
                 methodProcessingContext,
                 dexItemFactory) ->
                 ImmutableList.of(
-                    new CfNew(factory.desugarMethodHandlesLookupType),
+                    new CfNew(factory.lookupType),
                     new CfStackInstruction(Opcode.Dup),
                     new CfInvoke(
                         Opcodes.INVOKESPECIAL,
                         factory.createMethod(
-                            factory.desugarMethodHandlesLookupType,
+                            factory.lookupType,
                             factory.createProto(factory.voidType),
                             factory.constructorMethodName),
                         false)))
         .build();
   }
 
-  public DesugarDescription computeInvokeMethodHandleLookupMethodReturningVarHandle(
-      DexItemFactory factory, CfInvoke invoke) {
-    return DesugarDescription.builder()
-        .setDesugarRewrite(
-            (freshLocalProvider,
-                localStackAllocator,
-                eventConsumer,
-                context,
-                methodProcessingContext,
-                dexItemFactory) ->
-                ImmutableList.of(
-                    new CfInvoke(
-                        Opcodes.INVOKEVIRTUAL,
-                        factory.createMethod(
-                            factory.desugarMethodHandlesLookupType,
-                            factory.createProto(
-                                factory.desugarVarHandleType,
-                                invoke.getMethod().getProto().getParameters()),
-                            invoke.getMethod().getName()),
-                        false)))
-        .build();
-  }
-
-  public DesugarDescription computeInvokeMethodHandleLookupMethodReturningMethodHandle(
-      DexItemFactory factory, CfInvoke invoke) {
-    return DesugarDescription.builder()
-        .setDesugarRewrite(
-            (freshLocalProvider,
-                localStackAllocator,
-                eventConsumer,
-                context,
-                methodProcessingContext,
-                dexItemFactory) ->
-                ImmutableList.of(
-                    new CfInvoke(
-                        Opcodes.INVOKEVIRTUAL,
-                        factory.createMethod(
-                            factory.desugarMethodHandlesLookupType,
-                            invoke.getMethod().getProto(),
-                            invoke.getMethod().getName()),
-                        false)))
-        .build();
-  }
-
   public DesugarDescription computeDesugarSignaturePolymorphicMethod(
       CfInvoke invoke, int coordinates) {
     return DesugarDescription.builder()
@@ -463,7 +410,7 @@
                 ? proto.returnType
                 : objectOrPrimitiveReturnType(proto.returnType),
             newParameters);
-    DexMethod newMethod = factory.createMethod(factory.desugarVarHandleType, newProto, name);
+    DexMethod newMethod = factory.createMethod(factory.varHandleType, newProto, name);
     builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newMethod, false));
     if (proto.returnType.isPrimitiveType() && !newProto.returnType.isPrimitiveType()) {
       assert proto.returnType.isPrimitiveType();
@@ -487,20 +434,38 @@
 
   @Override
   // TODO(b/247076137): Is synthesizeClasses needed? Can DesugarVarHandle be created during
-  //  desugaring instead?
+  //  desugaring instead? For R8 creating up-front and replacing the library definition seems to
+  //  be the way to go.
   public void synthesizeClasses(
       ClassSynthesisDesugaringContext processingContext,
       CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
     DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
-    if (flags.hasReadVarHandleReferenceFromProgramClass()) {
+    synthesizeClassIfReferenced(
+        flags,
+        DexApplicationReadFlags::hasReadMethodHandlesLookupReferenceFromProgramClass,
+        DexApplicationReadFlags::getMethodHandlesLookupWitnesses,
+        classes -> ensureMethodHandlesLookupClass(eventConsumer, classes));
+    synthesizeClassIfReferenced(
+        flags,
+        DexApplicationReadFlags::hasReadVarHandleReferenceFromProgramClass,
+        DexApplicationReadFlags::getVarHandleWitnesses,
+        classes -> ensureVarHandleClass(eventConsumer, classes));
+  }
+
+  private void synthesizeClassIfReferenced(
+      DexApplicationReadFlags flags,
+      Predicate<DexApplicationReadFlags> hasReadReferenceFromProgramClass,
+      Function<DexApplicationReadFlags, Set<DexType>> getWitnesses,
+      Consumer<List<ProgramDefinition>> consumeProgramWitnesses) {
+    if (hasReadReferenceFromProgramClass.test(flags)) {
       List<ProgramDefinition> classes = new ArrayList<>();
-      for (DexType varHandleWitness : flags.getVarHandleWitnesses()) {
-        DexClass dexClass = appView.contextIndependentDefinitionFor(varHandleWitness);
+      for (DexType witness : getWitnesses.apply(flags)) {
+        DexClass dexClass = appView.contextIndependentDefinitionFor(witness);
         assert dexClass != null;
         assert dexClass.isProgramClass();
         classes.add(dexClass.asProgramClass());
       }
-      ensureVarHandleClass(eventConsumer, classes);
+      consumeProgramWitnesses.accept(classes);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
index 112f246..90c8eb5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
@@ -50,13 +50,14 @@
 public final class VarHandleDesugaringMethods {
 
   public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
-    factory.createSynthesizedType("Lcom/android/tools/r8/DesugarVarHandle;");
     factory.createSynthesizedType("Ljava/lang/Byte;");
     factory.createSynthesizedType("Ljava/lang/ClassCastException;");
     factory.createSynthesizedType("Ljava/lang/Integer;");
     factory.createSynthesizedType("Ljava/lang/Long;");
     factory.createSynthesizedType("Ljava/lang/RuntimeException;");
     factory.createSynthesizedType("Ljava/lang/Short;");
+    factory.createSynthesizedType("Ljava/lang/UnsupportedOperationException;");
+    factory.createSynthesizedType("Ljava/lang/invoke/VarHandle;");
     factory.createSynthesizedType("Ljava/lang/reflect/Field;");
     factory.createSynthesizedType("Lsun/misc/Unsafe;");
   }
@@ -305,7 +306,7 @@
         factory.createMethod(
             builder.getType(),
             factory.createProto(
-                factory.createType(factory.createString("Lcom/android/tools/r8/DesugarVarHandle;")),
+                factory.createType(factory.createString("Ljava/lang/invoke/VarHandle;")),
                 factory.createType(factory.createString("Ljava/lang/Class;")),
                 factory.createType(factory.createString("Ljava/lang/String;")),
                 factory.createType(factory.createString("Ljava/lang/Class;"))),
@@ -371,7 +372,7 @@
         4,
         ImmutableList.of(
             label0,
-            new CfNew(factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+            new CfNew(factory.createType("Ljava/lang/invoke/VarHandle;")),
             new CfStackInstruction(CfStackInstruction.Opcode.Dup),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 2),
@@ -379,7 +380,7 @@
             new CfInvoke(
                 183,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(
                         factory.voidType, factory.classType, factory.stringType, factory.classType),
                     factory.createString("<init>")),
@@ -450,7 +451,7 @@
             new CfCheckCast(factory.createType("Lsun/misc/Unsafe;")),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             label4,
@@ -458,7 +459,7 @@
             new CfLoad(ValueType.OBJECT, 1),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("recv"))),
             label5,
@@ -473,7 +474,7 @@
                 false),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             label6,
@@ -481,13 +482,13 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("recv"))),
             new CfInvoke(
@@ -500,7 +501,7 @@
             new CfNumberConversion(NumericType.INT, NumericType.LONG),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             label7,
@@ -521,6 +522,9 @@
     CfLabel label7 = new CfLabel();
     CfLabel label8 = new CfLabel();
     CfLabel label9 = new CfLabel();
+    CfLabel label10 = new CfLabel();
+    CfLabel label11 = new CfLabel();
+    CfLabel label12 = new CfLabel();
     return new CfCode(
         method.holder,
         4,
@@ -571,7 +575,7 @@
             new CfCheckCast(factory.createType("Lsun/misc/Unsafe;")),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             label4,
@@ -579,7 +583,7 @@
             new CfLoad(ValueType.OBJECT, 1),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("recv"))),
             label5,
@@ -606,15 +610,116 @@
                 false),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             label7,
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.classType,
+                    factory.createProto(factory.booleanType),
+                    factory.createString("isPrimitive")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.INT, label10),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfStaticFieldRead(
+                factory.createField(
+                    factory.createType("Ljava/lang/Integer;"),
+                    factory.classType,
+                    factory.createString("TYPE"))),
+            new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label10),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfStaticFieldRead(
+                factory.createField(
+                    factory.createType("Ljava/lang/Long;"),
+                    factory.classType,
+                    factory.createString("TYPE"))),
+            new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label10),
+            label8,
+            new CfNew(factory.createType("Ljava/lang/UnsupportedOperationException;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfNew(factory.stringBuilderType),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.voidType),
+                    factory.createString("<init>")),
+                false),
+            new CfConstString(factory.createString("Using a VarHandle for a field of type '")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfLoad(ValueType.OBJECT, 3),
+            label9,
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.classType,
+                    factory.createProto(factory.stringType),
+                    factory.createString("getName")),
+                false),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfConstString(
+                factory.createString(
+                    "' requires native VarHandle support available from Android 13. VarHandle"
+                        + " desugaring only supports primitive types int and long and reference"
+                        + " types.")),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringBuilderType, factory.stringType),
+                    factory.createString("append")),
+                false),
+            new CfInvoke(
+                182,
+                factory.createMethod(
+                    factory.stringBuilderType,
+                    factory.createProto(factory.stringType),
+                    factory.createString("toString")),
+                false),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Ljava/lang/UnsupportedOperationException;"),
+                    factory.createProto(factory.voidType, factory.stringType),
+                    factory.createString("<init>")),
+                false),
+            new CfThrow(),
+            label10,
+            new CfFrame(
+                new Int2ObjectAVLTreeMap<>(
+                    new int[] {0, 1, 2, 3, 4, 5},
+                    new FrameType[] {
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
+                      FrameType.initializedNonNullReference(factory.classType),
+                      FrameType.initializedNonNullReference(factory.stringType),
+                      FrameType.initializedNonNullReference(factory.classType),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/lang/reflect/Field;")),
+                      FrameType.initializedNonNullReference(
+                          factory.createType("Ljava/lang/reflect/Field;"))
+                    })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
@@ -637,12 +742,12 @@
                 false),
             new CfInstanceFieldWrite(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
-            label8,
+            label11,
             new CfReturnVoid(),
-            label9),
+            label12),
         ImmutableList.of(),
         ImmutableList.of());
   }
@@ -667,7 +772,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -680,14 +785,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.OBJECT, 0),
@@ -697,7 +802,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.intType, factory.objectType, factory.booleanType),
                     factory.createString("toIntIfPossible")),
                 false),
@@ -707,7 +812,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.intType, factory.objectType, factory.booleanType),
                     factory.createString("toIntIfPossible")),
                 false),
@@ -731,7 +836,7 @@
                     new int[] {0, 1, 2, 3},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.initializedNonNullReference(factory.objectType)
@@ -739,7 +844,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -752,14 +857,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.OBJECT, 0),
@@ -769,7 +874,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.longType, factory.objectType, factory.booleanType),
                     factory.createString("toLongIfPossible")),
                 false),
@@ -779,7 +884,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.longType, factory.objectType, factory.booleanType),
                     factory.createString("toLongIfPossible")),
                 false),
@@ -803,7 +908,7 @@
                     new int[] {0, 1, 2, 3},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.initializedNonNullReference(factory.objectType)
@@ -811,14 +916,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.OBJECT, 2),
@@ -857,7 +962,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -870,14 +975,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.INT, 2),
@@ -901,7 +1006,7 @@
                     new int[] {0, 1, 2, 3},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType(),
                       FrameType.intType()
@@ -909,7 +1014,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -922,14 +1027,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.INT, 2),
@@ -955,7 +1060,7 @@
                     new int[] {0, 1, 2, 3},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType(),
                       FrameType.intType()
@@ -981,7 +1086,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(
                         factory.booleanType,
                         factory.objectType,
@@ -1010,7 +1115,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1023,14 +1128,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.LONG, 2),
@@ -1054,7 +1159,7 @@
                     new int[] {0, 1, 2, 3, 4, 5},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.longType(),
                       FrameType.longHighType(),
@@ -1082,7 +1187,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(
                         factory.booleanType,
                         factory.objectType,
@@ -1138,7 +1243,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1151,14 +1256,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1182,13 +1287,13 @@
                     new int[] {0, 1},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1201,14 +1306,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1232,20 +1337,20 @@
                     new int[] {0, 1},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1277,7 +1382,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1290,14 +1395,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1314,13 +1419,13 @@
                     new int[] {0, 1},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1333,14 +1438,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1358,21 +1463,21 @@
                     new int[] {0, 1},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1386,7 +1491,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.intType, factory.objectType, factory.booleanType),
                     factory.createString("toIntIfPossible")),
                 false),
@@ -1412,7 +1517,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1425,14 +1530,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1449,13 +1554,13 @@
                     new int[] {0, 1},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1468,14 +1573,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1493,21 +1598,21 @@
                     new int[] {0, 1},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfInvoke(
@@ -1521,7 +1626,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.longType, factory.objectType, factory.booleanType),
                     factory.createString("toLongIfPossible")),
                 false),
@@ -1548,7 +1653,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1566,14 +1671,14 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.intType, factory.objectType, factory.booleanType),
                     factory.createString("toIntIfPossible")),
                 false),
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.voidType, factory.objectType, factory.intType),
                     factory.createString("set")),
                 false),
@@ -1584,14 +1689,14 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1609,14 +1714,14 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.longType, factory.objectType, factory.booleanType),
                     factory.createString("toLongIfPossible")),
                 false),
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.voidType, factory.objectType, factory.longType),
                     factory.createString("set")),
                 false),
@@ -1627,21 +1732,21 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.OBJECT, 2),
@@ -1659,7 +1764,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.initializedNonNullReference(factory.objectType)
                     })),
@@ -1686,7 +1791,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1699,14 +1804,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.INT, 2),
@@ -1725,14 +1830,14 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1745,14 +1850,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.INT, 2),
@@ -1772,7 +1877,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -1789,7 +1894,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.voidType, factory.objectType, factory.objectType),
                     factory.createString("set")),
                 false),
@@ -1799,7 +1904,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -1826,7 +1931,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1839,14 +1944,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.LONG, 2),
@@ -1865,7 +1970,7 @@
                     new int[] {0, 1, 2, 3},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.longType(),
                       FrameType.longHighType()
@@ -1873,7 +1978,7 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.classType,
                     factory.createString("type"))),
             new CfStaticFieldRead(
@@ -1887,7 +1992,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.createType("Ljava/lang/RuntimeException;")),
                     factory.createString("desugarWrongMethodTypeException")),
                 false),
@@ -1898,7 +2003,7 @@
                     new int[] {0, 1, 2, 3},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.longType(),
                       FrameType.longHighType()
@@ -1906,14 +2011,14 @@
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createType("Lsun/misc/Unsafe;"),
                     factory.createString("U"))),
             new CfLoad(ValueType.OBJECT, 1),
             new CfLoad(ValueType.OBJECT, 0),
             new CfInstanceFieldRead(
                 factory.createField(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.longType,
                     factory.createString("offset"))),
             new CfLoad(ValueType.LONG, 2),
@@ -1938,7 +2043,7 @@
                     new int[] {0, 1, 2, 3},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.longType(),
                       FrameType.longHighType()
@@ -1988,7 +2093,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -2012,7 +2117,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -2036,7 +2141,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -2060,7 +2165,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -2083,7 +2188,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -2091,7 +2196,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.createType("Ljava/lang/RuntimeException;")),
                     factory.createString("desugarWrongMethodTypeException")),
                 false),
@@ -2132,7 +2237,7 @@
                     new int[] {0, 1, 2},
                     new FrameType[] {
                       FrameType.initializedNonNullReference(
-                          factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+                          factory.createType("Ljava/lang/invoke/VarHandle;")),
                       FrameType.initializedNonNullReference(factory.objectType),
                       FrameType.intType()
                     })),
@@ -2142,7 +2247,7 @@
             new CfInvoke(
                 182,
                 factory.createMethod(
-                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createType("Ljava/lang/invoke/VarHandle;"),
                     factory.createProto(factory.intType, factory.objectType, factory.booleanType),
                     factory.createString("toIntIfPossible")),
                 false),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 7327231..d0db72d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -102,7 +102,8 @@
       assert next.isArgument();
       if (argumentInfo.isRewrittenTypeInfo()) {
         RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
-        DexType enumType = getEnumTypeOrNull(rewrittenTypeInfo.getOldType().toBaseType(factory));
+        DexType enumType =
+            getEnumClassTypeOrNull(rewrittenTypeInfo.getOldType().toBaseType(factory));
         if (enumType != null) {
           convertedEnums.put(next, enumType);
         }
@@ -142,7 +143,7 @@
 
         if (instruction.isInitClass()) {
           InitClass initClass = instruction.asInitClass();
-          DexType enumType = getEnumTypeOrNull(initClass.getClassValue());
+          DexType enumType = getEnumClassTypeOrNull(initClass.getClassValue());
           if (enumType != null) {
             iterator.removeOrReplaceByDebugLocalRead();
           }
@@ -154,7 +155,7 @@
           if (!ifInstruction.isZeroTest()) {
             for (int operandIndex = 0; operandIndex < 2; operandIndex++) {
               Value operand = ifInstruction.getOperand(operandIndex);
-              DexType enumType = getEnumTypeOrNull(operand, convertedEnums);
+              DexType enumType = getEnumClassTypeOrNull(operand, convertedEnums);
               if (enumType != null) {
                 int otherOperandIndex = 1 - operandIndex;
                 Value otherOperand = ifInstruction.getOperand(otherOperandIndex);
@@ -179,7 +180,7 @@
         //   also in the unboxed enum class.
         if (instruction.isInvokeMethodWithReceiver()) {
           InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
-          DexType enumType = getEnumTypeOrNull(invoke.getReceiver(), convertedEnums);
+          DexType enumType = getEnumClassTypeOrNull(invoke.getReceiver(), convertedEnums);
           DexMethod invokedMethod = invoke.getInvokedMethod();
           if (enumType != null) {
             if (invokedMethod == factory.enumMembers.ordinalMethod
@@ -217,7 +218,7 @@
             // Rewrites stringBuilder.append(enumInstance) as if it was
             // stringBuilder.append(String.valueOf(unboxedEnumInstance));
             Value enumArg = invoke.getArgument(1);
-            DexType enumArgType = getEnumTypeOrNull(enumArg, convertedEnums);
+            DexType enumArgType = getEnumClassTypeOrNull(enumArg, convertedEnums);
             if (enumArgType != null) {
               ProgramMethod stringValueOfMethod =
                   getLocalUtilityClass(enumArgType).ensureStringValueOfMethod(appView);
@@ -330,7 +331,7 @@
         // Rewrite array accesses from MyEnum[] (OBJECT) to int[] (INT).
         if (instruction.isArrayAccess()) {
           ArrayAccess arrayAccess = instruction.asArrayAccess();
-          DexType enumType = getEnumTypeOrNull(arrayAccess, convertedEnums);
+          DexType enumType = getEnumArrayTypeOrNull(arrayAccess, convertedEnums);
           if (enumType != null) {
             if (arrayAccess.hasOutValue()) {
               affectedPhis.addAll(arrayAccess.outValue().uniquePhiUsers());
@@ -403,14 +404,14 @@
       if (invokedMethod == factory.objectsMethods.requireNonNull) {
         assert invoke.arguments().size() == 1;
         Value argument = invoke.getFirstArgument();
-        DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+        DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
         if (enumType != null) {
           rewriteNullCheck(instructionIterator, invoke);
         }
       } else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
         assert invoke.arguments().size() == 2;
         Value argument = invoke.getFirstArgument();
-        DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+        DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
         if (enumType != null) {
           replaceEnumInvoke(
               instructionIterator,
@@ -426,7 +427,7 @@
       if (invokedMethod == factory.stringMembers.valueOf) {
         assert invoke.arguments().size() == 1;
         Value argument = invoke.getFirstArgument();
-        DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+        DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
         if (enumType != null) {
           ProgramMethod stringValueOfMethod =
               getLocalUtilityClass(enumType).ensureStringValueOfMethod(appView);
@@ -445,7 +446,7 @@
       } else if (invokedMethod == factory.javaLangSystemMembers.identityHashCode) {
         assert invoke.arguments().size() == 1;
         Value argument = invoke.getFirstArgument();
-        DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+        DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
         if (enumType != null) {
           invoke.outValue().replaceUsers(argument);
           instructionIterator.removeOrReplaceByDebugLocalRead();
@@ -470,7 +471,7 @@
           CheckNotNullEnumUnboxerMethodClassification checkNotNullClassification =
               classification.asCheckNotNullClassification();
           Value argument = invoke.getArgument(checkNotNullClassification.getArgumentIndex());
-          DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+          DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
           if (enumType != null) {
             InvokeStatic replacement =
                 InvokeStatic.builder()
@@ -529,7 +530,7 @@
 
   private Value fixNullsInBlockPhis(IRCode code, BasicBlock block, Value zeroConstValue) {
     for (Phi phi : block.getPhis()) {
-      if (getEnumTypeOrNull(phi.getType()) != null) {
+      if (getEnumClassTypeOrNull(phi.getType()) != null) {
         for (int i = 0; i < phi.getOperands().size(); i++) {
           Value operand = phi.getOperand(i);
           if (operand.getType().isNullType()) {
@@ -585,26 +586,26 @@
     return true;
   }
 
-  private DexType getEnumTypeOrNull(Value receiver, Map<Instruction, DexType> convertedEnums) {
+  private DexType getEnumClassTypeOrNull(Value receiver, Map<Instruction, DexType> convertedEnums) {
     TypeElement type = receiver.getType();
-    if (type.isInt() || (type.isArrayType() && type.asArrayType().getBaseType().isInt())) {
+    if (type.isInt()) {
       return receiver.isPhi() ? null : convertedEnums.get(receiver.getDefinition());
     }
-    return getEnumTypeOrNull(type);
+    return getEnumClassTypeOrNull(type);
   }
 
-  private DexType getEnumTypeOrNull(TypeElement type) {
+  private DexType getEnumClassTypeOrNull(TypeElement type) {
     if (!type.isClassType()) {
       return null;
     }
-    return getEnumTypeOrNull(type.asClassType().getClassType());
+    return getEnumClassTypeOrNull(type.asClassType().getClassType());
   }
 
-  private DexType getEnumTypeOrNull(DexType type) {
+  private DexType getEnumClassTypeOrNull(DexType type) {
     return unboxedEnumsData.isUnboxedEnum(type) ? type : null;
   }
 
-  private DexType getEnumTypeOrNull(
+  private DexType getEnumArrayTypeOrNull(
       ArrayAccess arrayAccess, Map<Instruction, DexType> convertedEnums) {
     ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
     if (arrayType == null) {
@@ -616,9 +617,13 @@
     }
     TypeElement baseType = arrayType.getBaseType();
     if (baseType.isClassType()) {
-      DexType classType = baseType.asClassType().getClassType();
-      return unboxedEnumsData.isUnboxedEnum(classType) ? classType : null;
+      return getEnumClassTypeOrNull(baseType.asClassType().getClassType());
     }
-    return getEnumTypeOrNull(arrayAccess.array(), convertedEnums);
+    if (arrayType.getBaseType().isInt()) {
+      return arrayAccess.array().isPhi()
+          ? null
+          : convertedEnums.get(arrayAccess.array().getDefinition());
+    }
+    return null;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 8e0d7d4..f9c13e9 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -70,6 +70,7 @@
             .setLine(0)
             .setMethod(originalMethod)
             .setCallerPosition(callerPosition)
+            .setIsD8R8Synthesized(true)
             .build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index b6ea11b..fc4fd33 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.naming.MappedRangeUtils.isInlineMappedRange;
 import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
 import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -128,9 +129,8 @@
     composingClassBuilder.compose(classNameMapper, classMapping);
   }
 
-
-  @Override
-  public String toString() {
+  public String finish() {
+    committed.finish();
     List<ComposingClassBuilder> classBuilders = new ArrayList<>(committed.classBuilders.values());
     classBuilders.sort(Comparator.comparing(ComposingClassBuilder::getOriginalName));
     StringBuilder sb = new StringBuilder();
@@ -167,6 +167,12 @@
     private final Map<ClassTypeNameAndMethodName, UpdateOutlineCallsiteInformation>
         outlineSourcePositionsUpdated = new HashMap<>();
 
+    /**
+     * Map of signatures that should be removed when finalizing the composed map. The key is the
+     * original name of a class.
+     */
+    private final Map<String, Set<Signature>> signaturesToRemove = new HashMap<>();
+
     private final List<String> preamble = new ArrayList<>();
 
     public void commit(ComposingData current, ClassNameMapper classNameMapper)
@@ -175,6 +181,7 @@
       commitClassBuilders(current, classNameMapper);
       commitRewriteFrameInformation(current, classNameMapper);
       commitOutlineCallsiteInformation(current, classNameMapper);
+      commitSignaturesToRemove(current);
     }
 
     private void commitClassBuilders(ComposingData current, ClassNameMapper classNameMapper)
@@ -211,6 +218,28 @@
       classBuilders = newClassBuilders;
     }
 
+    private void commitSignaturesToRemove(ComposingData current) {
+      current.signaturesToRemove.forEach(
+          (originalName, signatures) -> {
+            signaturesToRemove.merge(
+                originalName,
+                signatures,
+                (signatures1, signatures2) -> {
+                  Set<Signature> joinedSignatures = Sets.newHashSet(signatures1);
+                  joinedSignatures.addAll(signatures2);
+                  return joinedSignatures;
+                });
+          });
+    }
+
+    public void addSignatureToRemove(
+        ComposingClassBuilder composingClassBuilder, Signature signature) {
+      signaturesToRemove
+          .computeIfAbsent(
+              composingClassBuilder.getOriginalName(), ignoreArgument(Sets::newHashSet))
+          .add(signature);
+    }
+
     private void commitRewriteFrameInformation(
         ComposingData current, ClassNameMapper classNameMapper) {
       // First update the existing frame information to have new class name mappings.
@@ -313,6 +342,25 @@
         return newTypeName == null ? typeReference : Reference.classFromTypeName(newTypeName);
       }
     }
+
+    public void finish() {
+      classBuilders.forEach(
+          (ignored, classBuilder) -> {
+            Set<Signature> signatures = signaturesToRemove.get(classBuilder.getOriginalName());
+            if (signatures == null) {
+              return;
+            }
+            signatures.forEach(
+                signature -> {
+                  if (signature.isFieldSignature()) {
+                    classBuilder.fieldMembers.remove(signature.asFieldSignature());
+                  } else {
+                    // TODO(b/241763080): Define removal of methods with and without signatures.
+                    throw new Unreachable();
+                  }
+                });
+          });
+    }
   }
 
   private static class ClassTypeNameAndMethodName {
@@ -417,6 +465,7 @@
     private final ComposingData committed;
     private final ComposingData current;
 
+    private final Map<String, ComposingClassBuilder> committedPreviousClassBuilders;
     private final ComposingClassBuilder committedPreviousClassBuilder;
     private final InternalOptions options;
 
@@ -431,7 +480,8 @@
       this.current = current;
       this.committed = committed;
       this.options = options;
-      committedPreviousClassBuilder = committed.classBuilders.get(originalName);
+      committedPreviousClassBuilders = committed.classBuilders;
+      committedPreviousClassBuilder = committedPreviousClassBuilders.get(originalName);
     }
 
     public String getOriginalName() {
@@ -465,20 +515,46 @@
                 fieldNaming
                     .computeResidualSignature(type -> inverseClassMapping.getOrDefault(type, type))
                     .asFieldSignature();
-            if (committedPreviousClassBuilder != null) {
-              MemberNaming existingMemberNaming =
-                  committedPreviousClassBuilder.fieldMembers.remove(originalSignature);
-              if (existingMemberNaming != null) {
-                fieldNamingToAdd =
-                    new MemberNaming(
-                        existingMemberNaming.getOriginalSignature(), residualSignature);
+            MemberNaming existingMemberNaming = getExistingMemberNaming(originalSignature);
+            if (existingMemberNaming != null) {
+              Signature existingOriginalSignature = existingMemberNaming.getOriginalSignature();
+              if (!existingOriginalSignature.isQualified() && originalSignature.isQualified()) {
+                String previousCommittedClassName =
+                    getPreviousCommittedClassName(originalSignature.toHolderFromQualified());
+                if (previousCommittedClassName != null) {
+                  existingOriginalSignature =
+                      existingOriginalSignature.toQualifiedSignature(previousCommittedClassName);
+                }
               }
+              fieldNamingToAdd = new MemberNaming(existingOriginalSignature, residualSignature);
             }
             MemberNaming existing = fieldMembers.put(residualSignature, fieldNamingToAdd);
             assert existing == null;
           });
     }
 
+    private String getPreviousCommittedClassName(String holder) {
+      ComposingClassBuilder composingClassBuilder = committedPreviousClassBuilders.get(holder);
+      return composingClassBuilder == null ? null : composingClassBuilder.getOriginalName();
+    }
+
+    private MemberNaming getExistingMemberNaming(FieldSignature originalSignature) {
+      ComposingClassBuilder composingClassBuilder =
+          originalSignature.isQualified()
+              ? committedPreviousClassBuilders.get(originalSignature.toHolderFromQualified())
+              : committedPreviousClassBuilder;
+      if (composingClassBuilder == null) {
+        return null;
+      }
+      FieldSignature signature =
+          (originalSignature.isQualified()
+                  ? originalSignature.toUnqualifiedSignature()
+                  : originalSignature)
+              .asFieldSignature();
+      current.addSignatureToRemove(composingClassBuilder, signature);
+      return composingClassBuilder.fieldMembers.get(signature);
+    }
+
     private void composeMethodNamings(
         ClassNamingForNameMapper mapper, ClassNameMapper classNameMapper)
         throws MappingComposeException {
diff --git a/src/main/java/com/android/tools/r8/naming/MappingComposer.java b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
index 636e699..2068902 100644
--- a/src/main/java/com/android/tools/r8/naming/MappingComposer.java
+++ b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
@@ -23,6 +23,6 @@
     for (ClassNameMapper classNameMapper : classNameMappers) {
       builder.compose(classNameMapper);
     }
-    return builder.toString();
+    return builder.finish();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 12606ff..318034f 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -194,6 +194,10 @@
     public abstract Signature computeResidualSignature(
         String renamedName, Function<String, String> typeNameMapper);
 
+    public abstract Signature toUnqualifiedSignature();
+
+    public abstract Signature toQualifiedSignature(String holder);
+
     public boolean isQualified() {
       return name.indexOf(JAVA_PACKAGE_SEPARATOR) != -1;
     }
@@ -294,6 +298,17 @@
     }
 
     @Override
+    public Signature toUnqualifiedSignature() {
+      return new FieldSignature(toUnqualifiedName(), type);
+    }
+
+    @Override
+    public Signature toQualifiedSignature(String holder) {
+      assert !isQualified();
+      return new FieldSignature(holder + "." + name, type);
+    }
+
+    @Override
     public boolean equals(Object o) {
       if (this == o) {
         return true;
@@ -480,6 +495,16 @@
     }
 
     @Override
+    public Signature toUnqualifiedSignature() {
+      return new MethodSignature(toUnqualifiedName(), type, parameters);
+    }
+
+    @Override
+    public Signature toQualifiedSignature(String holder) {
+      return new MethodSignature(holder + "." + name, type, parameters);
+    }
+
+    @Override
     public boolean isMethodSignature() {
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java
new file mode 100644
index 0000000..cc9c83f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.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.naming;
+
+import com.android.tools.r8.graph.AppView;
+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.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+
+// Naming lens for VarHandle desugaring rewriting. Rewriting java.lang.invoke.MethodHandles$Lookup
+// to com.android.tools.r8.DesugarMethodHandlesLookup.
+public class VarHandleDesugaringRewritingNamingLens extends NonIdentityNamingLens {
+
+  private final DexItemFactory factory;
+  private final NamingLens namingLens;
+
+  public static NamingLens createVarHandleDesugaringRewritingNamingLens(AppView<?> appView) {
+    if (appView.options().shouldDesugarVarHandle()
+        && (appView
+                    .appInfo()
+                    .definitionForWithoutExistenceAssert(appView.dexItemFactory().lookupType)
+                != null
+            || appView
+                    .appInfo()
+                    .definitionForWithoutExistenceAssert(appView.dexItemFactory().varHandleType)
+                != null)) {
+      return new VarHandleDesugaringRewritingNamingLens(appView);
+    }
+    return appView.getNamingLens();
+  }
+
+  public VarHandleDesugaringRewritingNamingLens(AppView<?> appView) {
+    super(appView.dexItemFactory());
+    this.factory = appView.dexItemFactory();
+    this.namingLens = appView.getNamingLens();
+  }
+
+  private boolean isRenamed(DexType type) {
+    return getRenaming(type) != null;
+  }
+
+  private DexString getRenaming(DexType type) {
+    assert type != factory.desugarMethodHandlesLookupType;
+    assert type != factory.desugarVarHandleType;
+    if (type == factory.lookupType) {
+      return factory.desugarMethodHandlesLookupType.descriptor;
+    } else if (type == factory.varHandleType) {
+      return factory.desugarVarHandleType.descriptor;
+    }
+    return null;
+  }
+
+  @Override
+  protected DexString internalLookupClassDescriptor(DexType type) {
+    DexString renaming = getRenaming(type);
+    return renaming != null ? renaming : namingLens.lookupDescriptor(type);
+  }
+
+  @Override
+  public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
+    assert !isRenamed(attribute.getInner());
+    return namingLens.lookupInnerName(attribute, options);
+  }
+
+  @Override
+  public DexString lookupName(DexMethod method) {
+    // VarHandle desugaring rewriting does not influence method name.
+    return namingLens.lookupName(method);
+  }
+
+  @Override
+  public DexString lookupName(DexField field) {
+    // VarHandle desugaring rewriting does not influence method name.
+    return namingLens.lookupName(field);
+  }
+
+  @Override
+  public boolean hasPrefixRewritingLogic() {
+    return namingLens.hasPrefixRewritingLogic();
+  }
+
+  @Override
+  public DexString prefixRewrittenType(DexType type) {
+    return namingLens.prefixRewrittenType(type);
+  }
+
+  @Override
+  public String lookupPackageName(String packageName) {
+    return namingLens.lookupPackageName(packageName);
+  }
+
+  @Override
+  public boolean verifyRenamingConsistentWithResolution(DexMethod item) {
+    return namingLens.verifyRenamingConsistentWithResolution(item);
+  }
+}
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 b479808..32878da 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -65,6 +65,7 @@
 import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
 import com.android.tools.r8.ir.optimize.MethodPoolCollection;
@@ -2389,7 +2390,16 @@
           .setTarget(invocationTarget)
           .setInvokeType(type)
           .setIsInterface(isInterface);
-      return forwardSourceCodeBuilder::build;
+      return (context, callerPosition) -> {
+        SyntheticPosition caller =
+            SyntheticPosition.builder()
+                .setLine(0)
+                .setMethod(method)
+                .setIsD8R8Synthesized(true)
+                .setCallerPosition(callerPosition)
+                .build();
+        return forwardSourceCodeBuilder.build(context, caller);
+      };
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 05837f0..d0f940b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -153,6 +153,12 @@
   }
 
   @Override
+  public DexMethod getPreviousMethodSignatureForMapping(DexMethod method) {
+    DexMethod orDefault = newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
+    return super.getPreviousMethodSignature(orDefault);
+  }
+
+  @Override
   protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
     return mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
   }
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 38e6b3f..49455c1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -149,7 +149,7 @@
     }
   }
 
-  public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V19;
+  public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V20;
 
   public static final int SUPPORTED_DEX_VERSION =
       AndroidApiLevel.LATEST.getDexVersion().getIntValue();
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 2915d14..c0a65b6 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -192,13 +192,15 @@
         boolean canUseDexPc) {
       DexEncodedMethod definition = method.getDefinition();
       DexMethod originalMethod =
-          appView.graphLens().getOriginalMethodSignature(method.getReference());
+          appView.graphLens().getOriginalMethodSignatureForMapping(method.getReference());
       MethodSignature originalSignature =
           MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != originalType);
 
       OneShotCollectionConsumer<MappingInformation> methodSpecificMappingInformation =
           OneShotCollectionConsumer.wrap(new ArrayList<>());
-      if (method.getDefinition().isD8R8Synthesized()) {
+      if (method.getDefinition().isD8R8Synthesized()
+          || (!mappedPositions.isEmpty()
+              && mappedPositions.get(0).getPosition().isD8R8Synthesized())) {
         methodSpecificMappingInformation.add(
             CompilerSynthesizedMappingInformation.builder().build());
       }
@@ -309,11 +311,10 @@
                 appView,
                 getOriginalMethodSignature,
                 getBuilder(),
-                firstPosition.getMethod(),
+                firstPosition,
                 residualSignature,
                 obfuscatedRange,
                 originalRange,
-                firstPosition.getCallerPosition(),
                 prunedInlinedClasses,
                 cardinalRangeCache);
         methodSpecificMappingInformation.consume(
@@ -338,12 +339,11 @@
                         appView,
                         getOriginalMethodSignature,
                         getBuilder(),
-                        position.getMethod(),
+                        position,
                         residualSignature,
                         nonCardinalRangeCache.get(
                             placeHolderLineToBeFixed, placeHolderLineToBeFixed),
                         nonCardinalRangeCache.get(position.getLine(), position.getLine()),
-                        position.getCallerPosition(),
                         prunedInlinedClasses,
                         cardinalRangeCache);
                   });
@@ -370,37 +370,36 @@
         AppView<?> appView,
         Function<DexMethod, MethodSignature> getOriginalMethodSignature,
         ClassNaming.Builder classNamingBuilder,
-        DexMethod method,
+        Position position,
         MethodSignature residualSignature,
         Range obfuscatedRange,
         Range originalLine,
-        Position caller,
         Map<DexType, String> prunedInlineHolder,
         CardinalPositionRangeAllocator cardinalRangeCache) {
-      MappedRange lastMappedRange =
-          classNamingBuilder.addMappedRange(
-              obfuscatedRange,
-              getOriginalMethodSignature.apply(method),
-              originalLine,
-              residualSignature.getName());
-      int inlineFramesCount = 0;
-      while (caller != null) {
+      MappedRange lastMappedRange = null;
+      int inlineFramesCount = -1;
+      do {
+        if (position.isD8R8Synthesized() && position.hasCallerPosition()) {
+          position = position.getCallerPosition();
+          continue;
+        }
         inlineFramesCount += 1;
-        String prunedClassSourceFileInfo =
-            appView.getPrunedClassSourceFileInfo(method.getHolderType());
+        DexType holderType = position.getMethod().getHolderType();
+        String prunedClassSourceFileInfo = appView.getPrunedClassSourceFileInfo(holderType);
         if (prunedClassSourceFileInfo != null) {
-          String originalValue =
-              prunedInlineHolder.put(method.getHolderType(), prunedClassSourceFileInfo);
+          String originalValue = prunedInlineHolder.put(holderType, prunedClassSourceFileInfo);
           assert originalValue == null || originalValue.equals(prunedClassSourceFileInfo);
         }
         lastMappedRange =
             classNamingBuilder.addMappedRange(
                 obfuscatedRange,
-                getOriginalMethodSignature.apply(caller.getMethod()),
-                cardinalRangeCache.get(
-                    Math.max(caller.getLine(), 0)), // Prevent against "no-position".
+                getOriginalMethodSignature.apply(position.getMethod()),
+                inlineFramesCount == 0
+                    ? originalLine
+                    : cardinalRangeCache.get(
+                        Math.max(position.getLine(), 0)), // Prevent against "no-position".
                 residualSignature.getName());
-        if (caller.isRemoveInnerFramesIfThrowingNpe()) {
+        if (position.isRemoveInnerFramesIfThrowingNpe()) {
           lastMappedRange.addMappingInformation(
               RewriteFrameMappingInformation.builder()
                   .addCondition(
@@ -411,8 +410,9 @@
                   .build(),
               Unreachable::raise);
         }
-        caller = caller.getCallerPosition();
-      }
+        position = position.getCallerPosition();
+      } while (position != null);
+      assert lastMappedRange != null;
       return lastMappedRange;
     }
 
diff --git a/src/test/examplesJava9/varhandle/InstanceBooleanField.java b/src/test/examplesJava9/varhandle/InstanceBooleanField.java
new file mode 100644
index 0000000..89c034a
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceBooleanField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceBooleanField {
+
+  private boolean field;
+
+  public static void testSet(VarHandle varHandle) {
+    System.out.println("testGet");
+
+    InstanceBooleanField instance = new InstanceBooleanField();
+    System.out.println(varHandle.get(instance));
+
+    // boolean and Boolean values.
+    varHandle.set(instance, true);
+    System.out.println((boolean) varHandle.get(instance));
+    varHandle.set(instance, Boolean.FALSE);
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void testCompareAndSet(VarHandle varHandle) {
+    System.out.println("testCompareAndSet");
+
+    InstanceBooleanField instance = new InstanceBooleanField();
+
+    // boolean and Boolean values.
+    varHandle.compareAndSet(instance, true, false);
+    System.out.println((boolean) varHandle.get(instance));
+    varHandle.compareAndSet(instance, false, true);
+    System.out.println((boolean) varHandle.get(instance));
+    varHandle.compareAndSet(instance, Boolean.TRUE, false);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, false, Boolean.TRUE);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Boolean.TRUE, Boolean.FALSE);
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+    VarHandle varHandle;
+    try {
+      varHandle =
+          MethodHandles.lookup().findVarHandle(InstanceBooleanField.class, "field", boolean.class);
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got UnsupportedOperationException");
+      return;
+    }
+
+    testSet(varHandle);
+    testCompareAndSet(varHandle);
+  }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceByteField.java b/src/test/examplesJava9/varhandle/InstanceByteField.java
new file mode 100644
index 0000000..242df89
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceByteField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceByteField {
+
+  private byte field;
+
+  public static void testSet(VarHandle varHandle) {
+    System.out.println("testGet");
+
+    InstanceByteField instance = new InstanceByteField();
+    System.out.println(varHandle.get(instance));
+
+    // byte and Byte values.
+    varHandle.set(instance, (byte) 1);
+    System.out.println((byte) varHandle.get(instance));
+    varHandle.set(instance, Byte.valueOf((byte) 2));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void testCompareAndSet(VarHandle varHandle) {
+    System.out.println("testCompareAndSet");
+
+    InstanceByteField instance = new InstanceByteField();
+
+    // byte and Byte values.
+    varHandle.compareAndSet(instance, (byte) 1, (byte) 2);
+    System.out.println((byte) varHandle.get(instance));
+    varHandle.compareAndSet(instance, (byte) 0, (byte) 1);
+    System.out.println((byte) varHandle.get(instance));
+    varHandle.compareAndSet(instance, Byte.valueOf((byte) 1), (byte) 2);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, (byte) 2, Byte.valueOf((byte) 3));
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Byte.valueOf((byte) 3), Byte.valueOf((byte) 4));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+    VarHandle varHandle;
+    try {
+      varHandle =
+          MethodHandles.lookup().findVarHandle(InstanceByteField.class, "field", byte.class);
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got UnsupportedOperationException");
+      return;
+    }
+
+    testSet(varHandle);
+    testCompareAndSet(varHandle);
+  }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceDoubleField.java b/src/test/examplesJava9/varhandle/InstanceDoubleField.java
new file mode 100644
index 0000000..5b59463
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceDoubleField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceDoubleField {
+
+  private double field;
+
+  public static void testSet(VarHandle varHandle) {
+    System.out.println("testGet");
+
+    InstanceDoubleField instance = new InstanceDoubleField();
+    System.out.println(varHandle.get(instance));
+
+    // double and Double values.
+    varHandle.set(instance, 1.0);
+    System.out.println((double) varHandle.get(instance));
+    varHandle.set(instance, Double.valueOf(2));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void testCompareAndSet(VarHandle varHandle) {
+    System.out.println("testCompareAndSet");
+
+    InstanceDoubleField instance = new InstanceDoubleField();
+
+    // double and Double values.
+    varHandle.compareAndSet(instance, 1.0, 2.0);
+    System.out.println((double) varHandle.get(instance));
+    varHandle.compareAndSet(instance, 0.0, 1.0);
+    System.out.println((double) varHandle.get(instance));
+    varHandle.compareAndSet(instance, Double.valueOf(1), 2.0);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, 2.0, Double.valueOf(3));
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Double.valueOf(3), Double.valueOf(4));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+    VarHandle varHandle;
+    try {
+      varHandle =
+          MethodHandles.lookup().findVarHandle(InstanceDoubleField.class, "field", double.class);
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got UnsupportedOperationException");
+      return;
+    }
+
+    testSet(varHandle);
+    testCompareAndSet(varHandle);
+  }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceFloatField.java b/src/test/examplesJava9/varhandle/InstanceFloatField.java
new file mode 100644
index 0000000..06960c4
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceFloatField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceFloatField {
+
+  private float field;
+
+  public static void testSet(VarHandle varHandle) {
+    System.out.println("testGet");
+
+    InstanceFloatField instance = new InstanceFloatField();
+    System.out.println(varHandle.get(instance));
+
+    // float and Float values.
+    varHandle.set(instance, 1.0f);
+    System.out.println((float) varHandle.get(instance));
+    varHandle.set(instance, Float.valueOf(2));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void testCompareAndSet(VarHandle varHandle) {
+    System.out.println("testCompareAndSet");
+
+    InstanceFloatField instance = new InstanceFloatField();
+
+    // float and Float values.
+    varHandle.compareAndSet(instance, 1.0f, 2.0f);
+    System.out.println((float) varHandle.get(instance));
+    varHandle.compareAndSet(instance, 0.0f, 1.0f);
+    System.out.println((float) varHandle.get(instance));
+    varHandle.compareAndSet(instance, Float.valueOf(1), 2.0f);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, 2.0f, Float.valueOf(3f));
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Float.valueOf(3), Float.valueOf(4));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+    VarHandle varHandle;
+    try {
+      varHandle =
+          MethodHandles.lookup().findVarHandle(InstanceFloatField.class, "field", float.class);
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got UnsupportedOperationException");
+      return;
+    }
+
+    testSet(varHandle);
+    testCompareAndSet(varHandle);
+  }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceObjectField.java b/src/test/examplesJava9/varhandle/InstanceObjectField.java
index 1b4a4c9..c951ed8 100644
--- a/src/test/examplesJava9/varhandle/InstanceObjectField.java
+++ b/src/test/examplesJava9/varhandle/InstanceObjectField.java
@@ -20,7 +20,7 @@
     }
 
     public String toString() {
-      return new StringBuilder().append("A(").append(i).append(")").toString();
+      return "A(" + i + ")";
     }
   }
 
diff --git a/src/test/examplesJava9/varhandle/InstanceShortField.java b/src/test/examplesJava9/varhandle/InstanceShortField.java
new file mode 100644
index 0000000..7890cf2
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceShortField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceShortField {
+
+  private short field;
+
+  public static void testSet(VarHandle varHandle) {
+    System.out.println("testGet");
+
+    InstanceShortField instance = new InstanceShortField();
+    System.out.println(varHandle.get(instance));
+
+    // short and Short values.
+    varHandle.set(instance, (short) 1);
+    System.out.println((short) varHandle.get(instance));
+    varHandle.set(instance, Short.valueOf((short) 2));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void testCompareAndSet(VarHandle varHandle) {
+    System.out.println("testCompareAndSet");
+
+    InstanceShortField instance = new InstanceShortField();
+
+    // short and Short values.
+    varHandle.compareAndSet(instance, (short) 1, (short) 2);
+    System.out.println((short) varHandle.get(instance));
+    varHandle.compareAndSet(instance, (short) 0, (short) 1);
+    System.out.println((short) varHandle.get(instance));
+    varHandle.compareAndSet(instance, Short.valueOf((short) 1), (short) 2);
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, (short) 2, Short.valueOf((short) 3));
+    System.out.println(varHandle.get(instance));
+    varHandle.compareAndSet(instance, Short.valueOf((short) 3), Short.valueOf((short) 4));
+    System.out.println(varHandle.get(instance));
+  }
+
+  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+    VarHandle varHandle;
+    try {
+      varHandle =
+          MethodHandles.lookup().findVarHandle(InstanceShortField.class, "field", short.class);
+    } catch (UnsupportedOperationException e) {
+      System.out.println("Got UnsupportedOperationException");
+      return;
+    }
+
+    testSet(varHandle);
+    testCompareAndSet(varHandle);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 5c73e63..49a22e5 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -124,8 +124,8 @@
 
   public static final String R8_TEST_BUCKET = "r8-test-results";
 
-  public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.3.jar";
-  public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.3.jar";
+  public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.4.jar";
+  public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.4.jar";
 
   public static final Path API_SAMPLE_JAR = Paths.get("tests", "r8_api_usage_sample.jar");
 
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceBooleanFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceBooleanFieldTest.java
new file mode 100644
index 0000000..d9fc4c4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceBooleanFieldTest.java
@@ -0,0 +1,62 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceBooleanFieldTest extends VarHandleDesugaringTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "testGet",
+          "false",
+          "true",
+          "false",
+          "testCompareAndSet",
+          "false",
+          "true",
+          "false",
+          "true",
+          "false");
+
+  private static final String MAIN_CLASS = VarHandle.InstanceBooleanField.typeName();
+  private static final String JAR_ENTRY = "varhandle/InstanceBooleanField.class";
+
+  @Override
+  protected String getMainClass() {
+    return MAIN_CLASS;
+  }
+
+  @Override
+  protected String getKeepRules() {
+    return "-keep class " + getMainClass() + "{ <fields>; }";
+  }
+
+  @Override
+  protected List<String> getJarEntries() {
+    return ImmutableList.of(JAR_ENTRY);
+  }
+
+  @Override
+  protected String getExpectedOutputForReferenceImplementation() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  protected String getExpectedOutputForDesugaringImplementation() {
+    return StringUtils.lines("Got UnsupportedOperationException");
+  }
+
+  @Override
+  protected boolean getTestWithDesugaring() {
+    return true;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceByteFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceByteFieldTest.java
new file mode 100644
index 0000000..a96b5a9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceByteFieldTest.java
@@ -0,0 +1,52 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceByteFieldTest extends VarHandleDesugaringTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines("testGet", "0", "1", "2", "testCompareAndSet", "0", "1", "2", "3", "4");
+
+  private static final String MAIN_CLASS = VarHandle.InstanceByteField.typeName();
+  private static final String JAR_ENTRY = "varhandle/InstanceByteField.class";
+
+  @Override
+  protected String getMainClass() {
+    return MAIN_CLASS;
+  }
+
+  @Override
+  protected String getKeepRules() {
+    return "-keep class " + getMainClass() + "{ <fields>; }";
+  }
+
+  @Override
+  protected List<String> getJarEntries() {
+    return ImmutableList.of(JAR_ENTRY);
+  }
+
+  @Override
+  protected String getExpectedOutputForReferenceImplementation() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  protected String getExpectedOutputForDesugaringImplementation() {
+    return StringUtils.lines("Got UnsupportedOperationException");
+  }
+
+  @Override
+  protected boolean getTestWithDesugaring() {
+    return true;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceDoubleFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceDoubleFieldTest.java
new file mode 100644
index 0000000..194f25f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceDoubleFieldTest.java
@@ -0,0 +1,53 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceDoubleFieldTest extends VarHandleDesugaringTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "testGet", "0.0", "1.0", "2.0", "testCompareAndSet", "0.0", "1.0", "2.0", "3.0", "4.0");
+
+  private static final String MAIN_CLASS = VarHandle.InstanceDoubleField.typeName();
+  private static final String JAR_ENTRY = "varhandle/InstanceDoubleField.class";
+
+  @Override
+  protected String getMainClass() {
+    return MAIN_CLASS;
+  }
+
+  @Override
+  protected String getKeepRules() {
+    return "-keep class " + getMainClass() + "{ <fields>; }";
+  }
+
+  @Override
+  protected List<String> getJarEntries() {
+    return ImmutableList.of(JAR_ENTRY);
+  }
+
+  @Override
+  protected String getExpectedOutputForReferenceImplementation() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  protected String getExpectedOutputForDesugaringImplementation() {
+    return StringUtils.lines("Got UnsupportedOperationException");
+  }
+
+  @Override
+  protected boolean getTestWithDesugaring() {
+    return true;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceFloatFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceFloatFieldTest.java
new file mode 100644
index 0000000..457753b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceFloatFieldTest.java
@@ -0,0 +1,53 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceFloatFieldTest extends VarHandleDesugaringTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "testGet", "0.0", "1.0", "2.0", "testCompareAndSet", "0.0", "1.0", "2.0", "3.0", "4.0");
+
+  private static final String MAIN_CLASS = VarHandle.InstanceFloatField.typeName();
+  private static final String JAR_ENTRY = "varhandle/InstanceFloatField.class";
+
+  @Override
+  protected String getMainClass() {
+    return MAIN_CLASS;
+  }
+
+  @Override
+  protected String getKeepRules() {
+    return "-keep class " + getMainClass() + "{ <fields>; }";
+  }
+
+  @Override
+  protected List<String> getJarEntries() {
+    return ImmutableList.of(JAR_ENTRY);
+  }
+
+  @Override
+  protected String getExpectedOutputForReferenceImplementation() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  protected String getExpectedOutputForDesugaringImplementation() {
+    return StringUtils.lines("Got UnsupportedOperationException");
+  }
+
+  @Override
+  protected boolean getTestWithDesugaring() {
+    return true;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceShortFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceShortFieldTest.java
new file mode 100644
index 0000000..a5947eb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceShortFieldTest.java
@@ -0,0 +1,52 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceShortFieldTest extends VarHandleDesugaringTestBase {
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines("testGet", "0", "1", "2", "testCompareAndSet", "0", "1", "2", "3", "4");
+
+  private static final String MAIN_CLASS = VarHandle.InstanceShortField.typeName();
+  private static final String JAR_ENTRY = "varhandle/InstanceShortField.class";
+
+  @Override
+  protected String getMainClass() {
+    return MAIN_CLASS;
+  }
+
+  @Override
+  protected String getKeepRules() {
+    return "-keep class " + getMainClass() + "{ <fields>; }";
+  }
+
+  @Override
+  protected List<String> getJarEntries() {
+    return ImmutableList.of(JAR_ENTRY);
+  }
+
+  @Override
+  protected String getExpectedOutputForReferenceImplementation() {
+    return EXPECTED_OUTPUT;
+  }
+
+  @Override
+  protected String getExpectedOutputForDesugaringImplementation() {
+    return StringUtils.lines("Got UnsupportedOperationException");
+  }
+
+  @Override
+  protected boolean getTestWithDesugaring() {
+    return true;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
index 6a73d66..bb4d99b 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.examples.jdk9.VarHandle;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.references.MethodReference;
@@ -65,6 +64,10 @@
 
   protected abstract String getExpectedOutputForReferenceImplementation();
 
+  protected String getExpectedOutputForDesugaringImplementation() {
+    return getExpectedOutputForReferenceImplementation();
+  }
+
   protected String getExpectedOutputForArtImplementation() {
     return getExpectedOutputForReferenceImplementation();
   }
@@ -111,7 +114,7 @@
     // forwarding of Unsafe.compareAndSwapObject.
     MethodReference firstBackportFromDesugarVarHandle =
         SyntheticItemsTestUtils.syntheticBackportWithForwardingMethod(
-            Reference.classFromDescriptor(DexItemFactory.desugarVarHandleDescriptorString),
+            Reference.classFromDescriptor("Ljava/lang/invoke/VarHandle;"),
             0,
             Reference.method(
                 Reference.classFromDescriptor("Lsun/misc/Unsafe;"),
@@ -198,7 +201,7 @@
                                   .getDexRuntimeVersion()
                                   .isNewerThanOrEqual(Version.V13_0_0)
                           ? getExpectedOutputForArtImplementation()
-                          : getExpectedOutputForReferenceImplementation()))
+                          : getExpectedOutputForDesugaringImplementation()))
           .inspect(this::inspect);
     } else {
       testForD8(parameters.getBackend())
@@ -220,6 +223,18 @@
   // TODO(b/247076137: Also turn on VarHandle desugaring for R8 tests.
   @Test
   public void testR8() throws Throwable {
+    // TODO(b/247076137: The "default" VM is acting up on some tests - skip these as they will
+    // be fixed when VarHandle desugaring is enabled for R8.
+    if (parameters.isDexRuntime()
+        && parameters.asDexRuntime().getVersion().isEqualTo(Version.DEFAULT)
+        && parameters.getApiLevel().equals(AndroidApiLevel.B)
+        && (this instanceof VarHandleDesugaringInstanceBooleanFieldTest
+            || this instanceof VarHandleDesugaringInstanceByteFieldTest
+            || this instanceof VarHandleDesugaringInstanceShortFieldTest
+            || this instanceof VarHandleDesugaringInstanceFloatFieldTest
+            || this instanceof VarHandleDesugaringInstanceDoubleFieldTest)) {
+      return;
+    }
     testForR8(parameters.getBackend())
         .applyIf(
             parameters.isDexRuntime(),
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
index ee1a991..0f3c23b 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
@@ -20,9 +20,9 @@
 public abstract class CodeGenerationBase extends TestBase {
 
   private static final Path GOOGLE_FORMAT_DIR =
-      Paths.get(ToolHelper.THIRD_PARTY_DIR, "google-java-format");
+      Paths.get(ToolHelper.THIRD_PARTY_DIR, "google", "google-java-format", "1.14.0");
   private static final Path GOOGLE_FORMAT_JAR =
-      GOOGLE_FORMAT_DIR.resolve("google-java-format-1.7-all-deps.jar");
+      GOOGLE_FORMAT_DIR.resolve("google-java-format-1.14.0-all-deps.jar");
 
   protected final DexItemFactory factory = new DexItemFactory();
 
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/NullAssignmentToArrayArgEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/NullAssignmentToArrayArgEnumUnboxingTest.java
new file mode 100644
index 0000000..fa5450f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/NullAssignmentToArrayArgEnumUnboxingTest.java
@@ -0,0 +1,90 @@
+// 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.enumunboxing;
+
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NullAssignmentToArrayArgEnumUnboxingTest extends EnumUnboxingTestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public boolean enumValueOptimization;
+
+  @Parameter(2)
+  public EnumKeepRules enumKeepRules;
+
+  @Parameters(name = "{0}, value opt.: {1}, keep: {2}")
+  public static List<Object[]> data() {
+    return enumUnboxingTestParameters();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepRules(enumKeepRules.getKeepRules())
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+        // We need to disable entirely inlining since not only the methods checkNotNull and
+        // contains should not be inlined, but also the synthetic method with the zero check
+        // replacing the checkNotNull method should not be inlined.
+        .addOptionsModification(opt -> opt.inlinerOptions().enableInlining = false)
+        .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("false", "true", "npe", "npe");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(contains(MyEnum.A, new MyEnum[] {MyEnum.B, MyEnum.C}));
+      System.out.println(contains(MyEnum.B, new MyEnum[] {MyEnum.B, MyEnum.C}));
+      try {
+        System.out.println(contains(MyEnum.B, null));
+      } catch (NullPointerException npe) {
+        System.out.println("npe");
+      }
+      try {
+        System.out.println(contains(null, new MyEnum[] {MyEnum.B, MyEnum.C}));
+      } catch (NullPointerException npe) {
+        System.out.println("npe");
+      }
+    }
+
+    static void checkNotNull(Object o, String msg) {
+      if (o == null) {
+        throw new NullPointerException(msg);
+      }
+    }
+
+    static boolean contains(MyEnum e, MyEnum[] contents) {
+      checkNotNull(e, "elem");
+      checkNotNull(contents, "array");
+      for (MyEnum content : contents) {
+        if (content == e) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  enum MyEnum {
+    A,
+    B,
+    C;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java b/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java
index 620e01b..0cb3d7f 100644
--- a/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java
+++ b/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java
@@ -29,6 +29,21 @@
   public static final JavaExampleClassProxy InstanceLongField =
       new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceLongField");
 
+  public static final JavaExampleClassProxy InstanceBooleanField =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceBooleanField");
+
+  public static final JavaExampleClassProxy InstanceByteField =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceByteField");
+
+  public static final JavaExampleClassProxy InstanceShortField =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceShortField");
+
+  public static final JavaExampleClassProxy InstanceFloatField =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceFloatField");
+
+  public static final JavaExampleClassProxy InstanceDoubleField =
+      new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceDoubleField");
+
   public static final JavaExampleClassProxy InstanceObjectField =
       new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceObjectField");
 
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
index 30b3240..5780485 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
@@ -79,6 +79,14 @@
     this.recv = recv;
     Field field = recv.getDeclaredField(name);
     this.type = field.getType();
+    if (type.isPrimitive() && type != int.class && type != long.class) {
+      throw new UnsupportedOperationException(
+          "Using a VarHandle for a field of type '"
+              + type.getName()
+              + "' requires native VarHandle support available from Android 13. "
+              + "VarHandle desugaring only supports primitive types int and long and "
+              + "reference types.");
+    }
     this.offset = U.objectFieldOffset(recv.getDeclaredField(name));
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
index b72d827..369bc51 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
@@ -128,7 +128,7 @@
       DexType holderType = invoke.getMethod().getHolderType();
       DexType rewrittenType = typeMap.getOrDefault(holderType, holderType);
       String rewrittenName =
-          rewrittenType == factory.desugarVarHandleType ? methodNameMap.apply(name) : name;
+          rewrittenType == factory.varHandleType ? methodNameMap.apply(name) : name;
       if (rewrittenType != holderType) {
         // TODO(b/261024278): If sharing this code also rewrite signature.
         return new CfInvoke(
@@ -179,8 +179,11 @@
         new InstructionTypeMapper(
             ImmutableMap.of(
                 factory.createType(
+                    "L" + DesugarMethodHandlesLookup.class.getTypeName().replace('.', '/') + ";"),
+                factory.lookupType,
+                factory.createType(
                     "L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + ";"),
-                factory.desugarVarHandleType,
+                factory.varHandleType,
                 factory.createType(
                     "L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + "$UnsafeStub;"),
                 factory.unsafeType),
@@ -194,16 +197,16 @@
 
   private DexEncodedMethod methodWithName(DexEncodedMethod method, String name) {
     DexType holder = method.getHolderType();
-    DexType desugarVarHandle = factory.desugarVarHandleType;
+    DexType varHandle = factory.varHandleType;
     DexType desugarVarHandleStub =
         factory.createType("L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + ";");
     // Map methods to be on the final DesugarVarHandle class.
     if (holder == desugarVarHandleStub) {
-      holder = desugarVarHandle;
+      holder = varHandle;
     }
     DexProto proto = method.getProto();
     if (proto.getReturnType() == desugarVarHandleStub) {
-      proto = factory.createProto(desugarVarHandle, proto.parameters);
+      proto = factory.createProto(varHandle, proto.parameters);
     }
     return DexEncodedMethod.syntheticBuilder(method)
         .setMethod(factory.createMethod(holder, proto, factory.createString(name)))
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
new file mode 100644
index 0000000..5f2237d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
@@ -0,0 +1,151 @@
+// 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.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepOption;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepAnnotationViaSuperTest extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("42");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public KeepAnnotationViaSuperTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(getInputClasses())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testWithRuleExtraction() throws Exception {
+    List<String> rules = getExtractedKeepRules();
+    System.out.println(rules);
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getInputClassesWithoutKeepAnnotations())
+        .addKeepRules(rules)
+        .addKeepMainRule(TestClass.class)
+        .addKeepRuntimeVisibleAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED)
+        .inspect(this::checkOutput);
+  }
+
+  public List<Class<?>> getInputClasses() {
+    return ImmutableList.of(
+        TestClass.class,
+        Base.class,
+        SubA.class,
+        SubB.class,
+        SubC.class,
+        Anno.class,
+        UnusedAnno.class);
+  }
+
+  public List<byte[]> getInputClassesWithoutKeepAnnotations() throws Exception {
+    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
+  }
+
+  public List<String> getExtractedKeepRules() throws Exception {
+    List<Class<?>> classes = getInputClasses();
+    List<String> rules = new ArrayList<>();
+    for (Class<?> clazz : classes) {
+      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
+    }
+    return rules;
+  }
+
+  private void checkOutput(CodeInspector inspector) {
+    assertThat(inspector.clazz(Base.class), isPresent());
+
+    ClassSubject classA = inspector.clazz(SubA.class);
+    assertThat(classA, isPresent());
+    assertThat(classA.annotation(Anno.class), isPresent());
+    assertThat(classA.annotation(UnusedAnno.class), isAbsent());
+
+    ClassSubject classB = inspector.clazz(SubB.class);
+    assertThat(classB, isPresent());
+    assertThat(classB.annotation(Anno.class), isPresent());
+
+    assertThat(inspector.clazz(SubC.class), isAbsent());
+  }
+
+  @Target({ElementType.TYPE})
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface Anno {
+    int value();
+  }
+
+  @Target({ElementType.TYPE})
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface UnusedAnno {
+    int value();
+  }
+
+  abstract static class Base {
+
+    @UsesReflection({
+      @KeepTarget(
+          extendsClassConstant = Base.class,
+          disallow = {KeepOption.ANNOTATION_REMOVAL})
+    })
+    public Base() {
+      Anno annotation = getClass().getAnnotation(Anno.class);
+      System.out.println(annotation.value());
+    }
+  }
+
+  @Anno(42)
+  @UnusedAnno(123)
+  static class SubA extends Base {}
+
+  @Anno(7)
+  static class SubB extends Base {}
+
+  // Unused.
+  @Anno(-1)
+  static class SubC extends Base {}
+
+  static class TestClass {
+
+    public static void main(String[] args) throws Exception {
+      Base b = System.nanoTime() > 0 ? new SubA() : new SubB();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
index 8743b48..f99f3bf 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.keepanno.annotations.KeepConstants;
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
 import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
 import com.android.tools.r8.keepanno.asm.KeepEdgeWriter;
@@ -36,6 +37,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.List;
 import java.util.Set;
@@ -138,6 +140,16 @@
             });
   }
 
+  public static List<byte[]> getInputClassesWithoutKeepAnnotations(Collection<Class<?>> classes)
+      throws Exception {
+    List<byte[]> transformed = new ArrayList<>(classes.size());
+    for (Class<?> clazz : classes) {
+      transformed.add(
+          transformer(clazz).removeAnnotations(KeepConstants::isKeepAnnotation).transform());
+    }
+    return transformed;
+  }
+
   @Test
   public void testAsmReader() throws Exception {
     assumeTrue(parameters.isCfRuntime());
@@ -147,11 +159,7 @@
     byte[] original = ToolHelper.getClassAsBytes(source);
     // Strip out all the annotations to ensure they are actually added again.
     byte[] stripped =
-        transformer(source)
-            .removeClassAnnotations()
-            .removeMethodAnnotations()
-            .removeFieldAnnotations()
-            .transform();
+        getInputClassesWithoutKeepAnnotations(Collections.singletonList(source)).get(0);
     // Manually add in the expected edges again.
     byte[] readded =
         transformer(stripped, clazz)
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
new file mode 100644
index 0000000..6f95aa6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
@@ -0,0 +1,123 @@
+// 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.keepanno;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepOption;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.junit.function.ThrowingRunnable;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepInvalidTargetTest extends TestBase {
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public KeepInvalidTargetTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  private void assertThrowsWith(ThrowingRunnable fn, Matcher<String> matcher) {
+    try {
+      fn.run();
+      fail("Expected run to fail");
+    } catch (KeepEdgeException e) {
+      assertThat(e.getMessage(), matcher);
+    } catch (Throwable e) {
+      fail("Expected run to fail with KeepEdgeException");
+    }
+  }
+
+  @Test
+  public void testInvalidClassDecl() throws Exception {
+    assertThrowsWith(
+        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleClassDeclarations.class),
+        allOf(
+            containsString("Multiple declarations"),
+            containsString("className"),
+            containsString("classConstant")));
+  }
+
+  static class MultipleClassDeclarations {
+
+    @UsesReflection(@KeepTarget(className = "foo", classConstant = MultipleClassDeclarations.class))
+    public static void main(String[] args) throws Exception {
+      System.out.println("Hello, world");
+    }
+  }
+
+  @Test
+  public void testInvalidExtendsDecl() throws Exception {
+    assertThrowsWith(
+        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleExtendsDeclarations.class),
+        allOf(
+            containsString("Multiple declarations"),
+            containsString("extendsClassName"),
+            containsString("extendsClassConstant")));
+  }
+
+  static class MultipleExtendsDeclarations {
+
+    @UsesReflection(
+        @KeepTarget(
+            extendsClassName = "foo",
+            extendsClassConstant = MultipleClassDeclarations.class))
+    public static void main(String[] args) throws Exception {
+      System.out.println("Hello, world");
+    }
+  }
+
+  @Test
+  public void testInvalidMemberDecl() throws Exception {
+    assertThrowsWith(
+        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleMemberDeclarations.class),
+        allOf(containsString("field"), containsString("method")));
+  }
+
+  static class MultipleMemberDeclarations {
+
+    @UsesReflection(@KeepTarget(classConstant = A.class, methodName = "foo", fieldName = "bar"))
+    public static void main(String[] args) throws Exception {
+      System.out.println("Hello, world");
+    }
+  }
+
+  @Test
+  public void testInvalidOptionsDecl() throws Exception {
+    assertThrowsWith(
+        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleOptionDeclarations.class),
+        allOf(containsString("options"), containsString("allow"), containsString("disallow")));
+  }
+
+  static class MultipleOptionDeclarations {
+
+    @UsesReflection(
+        @KeepTarget(
+            classConstant = A.class,
+            allow = {KeepOption.OPTIMIZATION},
+            disallow = {KeepOption.SHRINKING}))
+    public static void main(String[] args) throws Exception {
+      System.out.println("Hello, world");
+    }
+  }
+
+  static class A {
+    // just a target.
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
index f9d98e3..d692791 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
@@ -65,12 +65,7 @@
   }
 
   public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<byte[]> transformed = new ArrayList<>(classes.size());
-    for (Class<?> clazz : classes) {
-      transformed.add(transformer(clazz).removeAllAnnotations().transform());
-    }
-    return transformed;
+    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
   }
 
   public List<String> getExtractedKeepRules() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
index 732f2d2..33f2531 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
@@ -64,12 +64,7 @@
   }
 
   public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<byte[]> transformed = new ArrayList<>(classes.size());
-    for (Class<?> clazz : classes) {
-      transformed.add(transformer(clazz).removeAllAnnotations().transform());
-    }
-    return transformed;
+    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
   }
 
   public List<String> getExtractedKeepRules() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
index b6cc560..bfc7fb5 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
@@ -64,12 +64,7 @@
   }
 
   public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<byte[]> transformed = new ArrayList<>(classes.size());
-    for (Class<?> clazz : classes) {
-      transformed.add(transformer(clazz).removeAllAnnotations().transform());
-    }
-    return transformed;
+    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
   }
 
   public List<String> getExtractedKeepRules() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
index 6739144..ee605b0 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
@@ -5,7 +5,9 @@
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -50,7 +52,9 @@
   @Test
   public void testWithRuleExtraction() throws Exception {
     List<String> rules = getExtractedKeepRules();
-    System.out.println(rules);
+    assertEquals(1, rules.size());
+    assertThat(rules.get(0), containsString("context: " + descriptor(A.class) + "foo()V"));
+    assertThat(rules.get(0), containsString("description: Keep the\\nstring-valued fields"));
     testForR8(parameters.getBackend())
         .addProgramClassFileData(getInputClassesWithoutAnnotations())
         .addKeepRules(rules)
@@ -66,12 +70,7 @@
   }
 
   public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<byte[]> transformed = new ArrayList<>(classes.size());
-    for (Class<?> clazz : classes) {
-      transformed.add(transformer(clazz).removeAllAnnotations().transform());
-    }
-    return transformed;
+    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
   }
 
   public List<String> getExtractedKeepRules() throws Exception {
@@ -94,7 +93,13 @@
     public String fieldA = "Hello, world";
     public Integer fieldB = 42;
 
-    @UsesReflection({@KeepTarget(classConstant = A.class, fieldType = "java.lang.String")})
+    @UsesReflection(
+        description = "Keep the\nstring-valued fields",
+        value = {
+          @KeepTarget(
+              className = "com.android.tools.r8.keepanno.KeepUsesReflectionOnFieldTest$A",
+              fieldType = "java.lang.String")
+        })
     public void foo() throws Exception {
       for (Field field : getClass().getDeclaredFields()) {
         if (field.getType().equals(String.class)) {
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index d9a6eaf..0386448 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -11,6 +11,8 @@
 import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
 import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
 import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -62,10 +64,10 @@
                     .build())
             .build();
     // Disallow will issue the full inverse of the known options, e.g., 'allowaccessmodification'.
-    assertEquals(
-        StringUtils.unixLines(
-            "-keep,allowshrinking,allowobfuscation,allowaccessmodification class * { *; }"),
-        extract(edge));
+    List<String> options =
+        ImmutableList.of("shrinking", "obfuscation", "accessmodification", "annotationremoval");
+    String allows = String.join(",allow", options);
+    assertEquals(StringUtils.unixLines("-keep,allow" + allows + " class * { *; }"), extract(edge));
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java
new file mode 100644
index 0000000..355dbf4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.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.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ComposeDuplicateMovedFieldTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+          "com.foo -> a:",
+          "    int some.other.Class.f1 -> g1",
+          "com.bar -> b:",
+          "    boolean f2 -> g2");
+  private static final String mappingBar =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+          "b -> c:",
+          "    int a.g1 -> h1",
+          "    boolean g2 -> h2",
+          "com.baz -> d:",
+          "    int a.g1 -> h1",
+          "    boolean b.g2 -> h2");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+          "com.bar -> c:",
+          "    boolean f2 -> h2",
+          "    int some.other.Class.f1 -> h1",
+          "com.baz -> d:",
+          "    boolean com.bar.f2 -> h2",
+          "    int some.other.Class.f1 -> h1",
+          "com.foo -> a:");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedFieldTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedFieldTest.java
new file mode 100644
index 0000000..69b78c7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedFieldTest.java
@@ -0,0 +1,59 @@
+// 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.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ComposeMovedFieldTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  private static final String mappingFoo =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+          "com.foo -> a:",
+          "    int some.other.Class.f1 -> g1",
+          "    boolean f2 -> g2");
+  private static final String mappingBar =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+          "com.bar -> b:",
+          "    int a.g1 -> h1",
+          "    boolean a.g2 -> h2");
+  private static final String mappingResult =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+          "com.bar -> b:",
+          "    boolean com.foo.f2 -> h2",
+          "    int some.other.Class.f1 -> h1",
+          "com.foo -> a:");
+
+  @Test
+  public void testCompose() throws Exception {
+    ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+    ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+    String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+    assertEquals(mappingResult, doubleToSingleQuote(composed));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarPrivateLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarPrivateLambdaRetraceTest.java
new file mode 100644
index 0000000..01f9b37
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarPrivateLambdaRetraceTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.naming.retrace.testclasses.DesugarInterfaceInstanceLambdaRetrace;
+import com.android.tools.r8.naming.retrace.testclasses.DesugarInterfaceInstanceLambdaRetrace.ConsumerDesugarLambda;
+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 DesugarPrivateLambdaRetraceTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  private final String fileName = "DesugarInterfaceInstanceLambdaRetrace.java";
+
+  public StackTrace expectedStackTrace =
+      StackTrace.builder()
+          .add(
+              StackTraceLine.builder()
+                  .setClassName(typeName(ConsumerDesugarLambda.class))
+                  .setMethodName("lambda$foo$0")
+                  .setFileName(fileName)
+                  .setLineNumber(16)
+                  .build())
+          .add(
+              StackTraceLine.builder()
+                  .setClassName(typeName(DesugarInterfaceInstanceLambdaRetrace.Main.class))
+                  .setMethodName("method1")
+                  .setFileName(fileName)
+                  .setLineNumber(26)
+                  .build())
+          .add(
+              StackTraceLine.builder()
+                  .setClassName(typeName(ConsumerDesugarLambda.class))
+                  .setMethodName("foo")
+                  .setFileName(fileName)
+                  .setLineNumber(13)
+                  .build())
+          .add(
+              StackTraceLine.builder()
+                  .setClassName(typeName(DesugarInterfaceInstanceLambdaRetrace.Main.class))
+                  .setMethodName("main")
+                  .setFileName(fileName)
+                  .setLineNumber(30)
+                  .build())
+          .build();
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addInnerClasses(DesugarInterfaceInstanceLambdaRetrace.class)
+        .run(TestRuntime.getDefaultCfRuntime(), DesugarInterfaceInstanceLambdaRetrace.Main.class)
+        .inspectStackTrace(stackTrace -> assertThat(stackTrace, isSame(expectedStackTrace)));
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    testForD8(parameters.getBackend())
+        .setMode(CompilationMode.DEBUG)
+        .addInnerClasses(DesugarInterfaceInstanceLambdaRetrace.class)
+        .setMinApi(parameters.getApiLevel())
+        .internalEnableMappingOutput()
+        .run(parameters.getRuntime(), DesugarInterfaceInstanceLambdaRetrace.Main.class)
+        .assertFailureWithErrorThatThrows(NullPointerException.class)
+        .inspectStackTrace(stackTrace -> assertThat(stackTrace, isSame(expectedStackTrace)));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index 9e361e6..5b4dcb2 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -66,15 +66,6 @@
     return retracedStackTraceLine.lineNumber > 0;
   }
 
-  private boolean filterSynthesizedBridgeMethod(StackTraceLine retracedStackTraceLine) {
-    if (!haveSeenLines.add(retracedStackTraceLine)) {
-      return false;
-    }
-    return !retracedStackTraceLine.className.contains("ResourceWrapper")
-        || !retracedStackTraceLine.methodName.contains("foo")
-        || retracedStackTraceLine.lineNumber != 0;
-  }
-
   @Test
   public void testSourceFileAndLineNumberTable() throws Exception {
     runTest(
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/testclasses/DesugarInterfaceInstanceLambdaRetrace.java b/src/test/java/com/android/tools/r8/naming/retrace/testclasses/DesugarInterfaceInstanceLambdaRetrace.java
new file mode 100644
index 0000000..301a994
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/testclasses/DesugarInterfaceInstanceLambdaRetrace.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.retrace.testclasses;
+
+public class DesugarInterfaceInstanceLambdaRetrace {
+
+  public interface ConsumerDesugarLambda {
+    String accept();
+
+    default void foo() {
+      Main.method1(
+          () -> {
+            if (System.currentTimeMillis() > 0) {
+              throw null;
+            }
+            return accept();
+          });
+    }
+  }
+
+  public static class Main {
+
+    public static void method1(ConsumerDesugarLambda iface) {
+      System.out.println(iface.accept());
+    }
+
+    public static void main(String[] args) {
+      ((ConsumerDesugarLambda) () -> "Hello World").foo();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
deleted file mode 100644
index 7e6306d..0000000
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
+++ /dev/null
@@ -1,166 +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.naming.retraceproguard;
-
-import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileName;
-import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileNameAndLineNumber;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.util.Collection;
-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 VerticalClassMergingRetraceTest extends RetraceTestBase {
-
-  @Parameters(name = "{0}, mode: {1}, compat: {2}")
-  public static Collection<Object[]> data() {
-    return buildParameters(
-        getTestParameters()
-            .withCfRuntimes()
-            // Runtimes prior to 8 will emit stack trace lines as:
-            // Exception in thread "main" java.lang.NullPointerException: throw with null exception
-            // 	at com.android.tools.r8.naming.retraceproguard.a.b(SourceFile)
-            // PG do not support retracing if no line number is specified.
-            .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
-            .withAllApiLevels()
-            .build(),
-        CompilationMode.values(),
-        BooleanUtils.values());
-  }
-
-  public VerticalClassMergingRetraceTest(
-      TestParameters parameters, CompilationMode mode, boolean compat) {
-    super(parameters, mode, compat);
-  }
-
-  @Override
-  public void configure(R8TestBuilder builder) {
-    builder.enableInliningAnnotations();
-  }
-
-  @Override
-  public Collection<Class<?>> getClasses() {
-    return ImmutableList.of(getMainClass(), ResourceWrapper.class, TintResources.class);
-  }
-
-  @Override
-  public Class<?> getMainClass() {
-    return MainApp.class;
-  }
-
-  private int expectedActualStackTraceHeight() {
-    // In RELEASE mode, a synthetic bridge will be added by vertical class merger.
-    int height = mode == CompilationMode.RELEASE ? 3 : 2;
-    if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) {
-      // Dalvik places a stack trace line in the bottom.
-      height += 1;
-    }
-    return height;
-  }
-
-  private boolean filterSynthesizedMethod(
-      StackTraceLine retracedStackTraceLine, MethodSubject syntheticMethod) {
-    if (syntheticMethod.isPresent()) {
-      String qualifiedMethodName =
-          retracedStackTraceLine.className + "." + retracedStackTraceLine.methodName;
-      return !qualifiedMethodName.equals(syntheticMethod.getOriginalName())
-          || retracedStackTraceLine.lineNumber > 0;
-    }
-    return true;
-  }
-
-  @Test
-  public void testSourceFileAndLineNumberTable() throws Exception {
-    Box<MethodSubject> syntheticMethod = new Box<>();
-    runTest(
-        ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
-        (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
-          // Even when SourceFile is present retrace replaces the file name in the stack trace.
-          StackTrace reprocessedStackTrace =
-              retracedStackTrace.filter(
-                  stackTraceLine -> filterSynthesizedMethod(stackTraceLine, syntheticMethod.get()));
-          assertThat(
-              reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
-              isSameExceptForFileName(
-                  expectedStackTrace.filter(this::isNotDalvikNativeStartMethod)));
-          assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
-        },
-        compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
-  }
-
-  @Test
-  public void testLineNumberTableOnly() throws Exception {
-    assumeTrue(compat);
-    assumeTrue(parameters.isDexRuntime());
-    Box<MethodSubject> syntheticMethod = new Box<>();
-    runTest(
-        ImmutableList.of("-keepattributes LineNumberTable"),
-        (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
-          StackTrace reprocessedStackTrace =
-              retracedStackTrace.filter(
-                  stackTraceLine -> filterSynthesizedMethod(stackTraceLine, syntheticMethod.get()));
-          assertThat(
-              reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
-              isSameExceptForFileName(
-                  expectedStackTrace.filter(this::isNotDalvikNativeStartMethod)));
-          assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
-        },
-        compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
-  }
-
-  private void setSyntheticMethod(
-      R8TestCompileResult compileResult, Box<MethodSubject> syntheticMethod) throws IOException {
-    compileResult.inspect(
-        inspector -> {
-          ClassSubject tintResourcesClassSubject = inspector.clazz(TintResources.class);
-          MethodSubject uniqueSyntheticMethod =
-              tintResourcesClassSubject.uniqueMethodThatMatches(
-                  method -> method.getAccessFlags().isSynthetic());
-          assertThat(uniqueSyntheticMethod, onlyIf(mode == CompilationMode.RELEASE, isPresent()));
-          syntheticMethod.set(uniqueSyntheticMethod);
-        });
-  }
-}
-
-class ResourceWrapper {
-  // Will be merged down, and represented as:
-  //     java.lang.String ...ResourceWrapper.foo() -> a
-  @NeverInline
-  String foo(boolean doThrow) {
-    if (doThrow) {
-      throw null;
-    }
-    return System.currentTimeMillis() > 0 ? "arg" : null;
-  }
-}
-
-class TintResources extends ResourceWrapper {}
-
-class MainApp {
-  public static void main(String[] args) {
-    TintResources t = new TintResources();
-    boolean doThrow = System.currentTimeMillis() > 0;
-    System.out.println(t.foo(doThrow));
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 1974bcd..7742119 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1463,32 +1463,51 @@
         });
   }
 
-  public ClassFileTransformer removeAllAnnotations() {
-    return removeClassAnnotations().removeMethodAnnotations().removeFieldAnnotations();
+  public interface AnnotationPredicate {
+    boolean test(String descriptor, boolean visible);
+
+    static AnnotationPredicate any() {
+      return (descriptor, visible) -> true;
+    }
   }
 
-  public ClassFileTransformer removeClassAnnotations() {
+  public ClassFileTransformer removeAllAnnotations() {
+    return removeAnnotations(AnnotationPredicate.any());
+  }
+
+  public ClassFileTransformer removeAnnotations(AnnotationPredicate predicate) {
+    return removeClassAnnotations(predicate)
+        .removeMethodAnnotations(predicate)
+        .removeFieldAnnotations(predicate);
+  }
+
+  public ClassFileTransformer removeClassAnnotations(AnnotationPredicate predicate) {
     return addClassTransformer(
         new ClassTransformer() {
           @Override
           public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
-            // Ignore all input annotations.
-            return null;
+            if (predicate.test(descriptor, visible)) {
+              return null;
+            }
+            return super.visitAnnotation(descriptor, visible);
           }
         });
   }
 
-  public ClassFileTransformer removeMethodAnnotations() {
+  public ClassFileTransformer removeMethodAnnotations(AnnotationPredicate predicate) {
     return addMethodTransformer(
         new MethodTransformer() {
           @Override
           public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
-            return null;
+            if (predicate.test(descriptor, visible)) {
+              return null;
+            }
+            return super.visitAnnotation(descriptor, visible);
           }
         });
   }
 
-  public ClassFileTransformer removeFieldAnnotations() {
+  public ClassFileTransformer removeFieldAnnotations(AnnotationPredicate predicate) {
     return addClassTransformer(
         new ClassTransformer() {
           @Override
@@ -1498,7 +1517,10 @@
             return new FieldVisitor(ASM_VERSION, fv) {
               @Override
               public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
-                return null;
+                if (predicate.test(descriptor, visible)) {
+                  return null;
+                }
+                return super.visitAnnotation(descriptor, visible);
               }
             };
           }
diff --git a/tools/asmifier.py b/tools/asmifier.py
index ae7d1e6..82b90af 100755
--- a/tools/asmifier.py
+++ b/tools/asmifier.py
@@ -10,7 +10,7 @@
 import sys
 import utils
 
-ASM_VERSION = '9.3'
+ASM_VERSION = '9.4'
 ASM_JAR = 'asm-' + ASM_VERSION + '.jar'
 ASM_UTIL_JAR = 'asm-util-' + ASM_VERSION + '.jar'