Support method name in keep edge AST, readers and writers.

Bug: b/248408342
Change-Id: I1f5218631a57e8271bf1f896614223ce9963100f
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/annotations/KeepConstants.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/annotations/KeepConstants.java
index c12ba98..9479a5d 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/annotations/KeepConstants.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/annotations/KeepConstants.java
@@ -30,5 +30,6 @@
     public static final Class<KeepTarget> CLASS = KeepTarget.class;
     public static final String DESCRIPTOR = getDescriptor(CLASS);
     public static final String classConstant = "classConstant";
+    public static final String methodName = "methodName";
   }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeReader.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeReader.java
index 3c705c7..840713d 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeReader.java
@@ -9,6 +9,10 @@
 import com.android.tools.r8.experimental.keepanno.ast.KeepEdge;
 import com.android.tools.r8.experimental.keepanno.ast.KeepEdgeException;
 import com.android.tools.r8.experimental.keepanno.ast.KeepItemPattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepItemPattern.Builder;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMembersPattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.experimental.keepanno.ast.KeepPreconditions;
 import com.android.tools.r8.experimental.keepanno.ast.KeepQualifiedClassNamePattern;
 import com.android.tools.r8.experimental.keepanno.ast.KeepTarget;
@@ -138,7 +142,8 @@
 
   private static class KeepTargetVisitor extends AnnotationVisitorBase {
     private final Parent<KeepTarget> parent;
-    private String classConstant = null;
+    private KeepQualifiedClassNamePattern classNamePattern = null;
+    private KeepMethodNamePattern methodName = null;
 
     public KeepTargetVisitor(Parent<KeepTarget> parent) {
       this.parent = parent;
@@ -147,7 +152,11 @@
     @Override
     public void visit(String name, Object value) {
       if (name.equals(Target.classConstant) && value instanceof Type) {
-        classConstant = ((Type) value).getClassName();
+        classNamePattern = KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
+        return;
+      }
+      if (name.equals(Target.methodName) && value instanceof String) {
+        methodName = KeepMethodNamePattern.exact((String) value);
         return;
       }
       super.visit(name, value);
@@ -155,18 +164,18 @@
 
     @Override
     public void visitEnd() {
-      if (classConstant != null) {
-        KeepTarget target =
-            KeepTarget.builder()
-                .setItem(
-                    KeepItemPattern.builder()
-                        .setClassPattern(KeepQualifiedClassNamePattern.exact(classConstant))
-                        .build())
-                .build();
-        parent.accept(target);
-        return;
+      Builder itemBuilder = KeepItemPattern.builder();
+      if (classNamePattern != null) {
+        itemBuilder.setClassPattern(classNamePattern);
       }
-      throw new KeepEdgeException("Unexpected failure to build @KeepTarget");
+      if (methodName != null) {
+        itemBuilder.setMembersPattern(
+            KeepMembersPattern.builder()
+                .addMethodPattern(KeepMethodPattern.builder().setNamePattern(methodName).build())
+                .build());
+      }
+      KeepTarget target = KeepTarget.builder().setItem(itemBuilder.build()).build();
+      parent.accept(target);
     }
   }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeWriter.java
index bda02d8..05d11ff 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/asm/KeepEdgeWriter.java
@@ -5,8 +5,10 @@
 
 import com.android.tools.r8.experimental.keepanno.annotations.KeepConstants;
 import com.android.tools.r8.experimental.keepanno.annotations.KeepConstants.Edge;
+import com.android.tools.r8.experimental.keepanno.annotations.KeepConstants.Target;
 import com.android.tools.r8.experimental.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.experimental.keepanno.ast.KeepEdge;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMethodNamePattern;
 import com.android.tools.r8.experimental.keepanno.ast.KeepPreconditions;
 import com.android.tools.r8.experimental.keepanno.ast.KeepQualifiedClassNamePattern;
 import com.android.tools.r8.experimental.keepanno.utils.Unimplemented;
@@ -66,6 +68,41 @@
                     } else {
                       throw new Unimplemented();
                     }
+                    if (!clazz.getExtendsPattern().isAny()) {
+                      throw new Unimplemented();
+                    }
+                    if (clazz.getMembersPattern().isNone()) {
+                      // Default is "no methods".
+                    } else if (clazz.getMembersPattern().isAll()) {
+                      throw new Unimplemented();
+                    } else {
+                      clazz
+                          .getMembersPattern()
+                          .forEach(
+                              field -> {
+                                throw new Unimplemented();
+                              },
+                              method -> {
+                                KeepMethodNamePattern methodNamePattern = method.getNamePattern();
+                                methodNamePattern.match(
+                                    () -> {
+                                      throw new Unimplemented();
+                                    },
+                                    exactMethodName -> {
+                                      targetVisitor.visit(Target.methodName, exactMethodName);
+                                      return null;
+                                    });
+                                if (!method.getAccessPattern().isAny()) {
+                                  throw new Unimplemented();
+                                }
+                                if (!method.getReturnTypePattern().isAny()) {
+                                  throw new Unimplemented();
+                                }
+                                if (!method.getParametersPattern().isAny()) {
+                                  throw new Unimplemented();
+                                }
+                              });
+                    }
                     return null;
                   });
           targetVisitor.visitEnd();
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepConsequences.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepConsequences.java
index 524192e..b2c0fdd 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepConsequences.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepConsequences.java
@@ -6,6 +6,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * Set of consequences of a keep edge.
@@ -68,4 +69,9 @@
   public int hashCode() {
     return targets.hashCode();
   }
+
+  @Override
+  public String toString() {
+    return targets.stream().map(Object::toString).collect(Collectors.joining(", "));
+  }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdge.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdge.java
index 7cd6d9f..c6127e4 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdge.java
@@ -76,4 +76,9 @@
   public int hashCode() {
     return Objects.hash(preconditions, consequences);
   }
+
+  @Override
+  public String toString() {
+    return "KeepEdge{" + "preconditions=" + preconditions + ", consequences=" + consequences + '}';
+  }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepExtendsPattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepExtendsPattern.java
index 1f6ef9d..b447925 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepExtendsPattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepExtendsPattern.java
@@ -52,6 +52,11 @@
     public int hashCode() {
       return System.identityHashCode(this);
     }
+
+    @Override
+    public String toString() {
+      return "*";
+    }
   }
 
   private static class KeepExtendsClassPattern extends KeepExtendsPattern {
@@ -84,6 +89,11 @@
     public int hashCode() {
       return pattern.hashCode();
     }
+
+    @Override
+    public String toString() {
+      return pattern.toString();
+    }
   }
 
   public static Builder builder() {
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepItemPattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepItemPattern.java
index 443d63b..60bb6e8 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepItemPattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepItemPattern.java
@@ -98,6 +98,11 @@
     public int hashCode() {
       return System.identityHashCode(this);
     }
+
+    @Override
+    public String toString() {
+      return "*";
+    }
   }
 
   public static class KeepClassPattern extends KeepItemPattern {
@@ -163,6 +168,18 @@
     public int hashCode() {
       return Objects.hash(qualifiedClassPattern, extendsPattern, membersPattern);
     }
+
+    @Override
+    public String toString() {
+      return "KeepClassPattern{"
+          + "qualifiedClassPattern="
+          + qualifiedClassPattern
+          + ", extendsPattern="
+          + extendsPattern
+          + ", membersPattern="
+          + membersPattern
+          + '}';
+    }
   }
 
   public abstract boolean isAny();
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMembersPattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMembersPattern.java
index f8b3515..ee4c75a 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMembersPattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMembersPattern.java
@@ -8,6 +8,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 public abstract class KeepMembersPattern {
 
@@ -100,6 +101,11 @@
     public int hashCode() {
       return System.identityHashCode(this);
     }
+
+    @Override
+    public String toString() {
+      return "*";
+    }
   }
 
   private static class KeepMembersNonePattern extends KeepMembersPattern {
@@ -137,6 +143,11 @@
     public int hashCode() {
       return System.identityHashCode(this);
     }
+
+    @Override
+    public String toString() {
+      return "<none>";
+    }
   }
 
   private static class KeepMembersSomePattern extends KeepMembersPattern {
@@ -184,6 +195,16 @@
     public int hashCode() {
       return Objects.hash(methods, fields);
     }
+
+    @Override
+    public String toString() {
+      return "KeepMembersSomePattern{"
+          + "methods={"
+          + methods.stream().map(Object::toString).collect(Collectors.joining(", "))
+          + "}, fields={"
+          + fields.stream().map(Object::toString).collect(Collectors.joining(", "))
+          + "}}";
+    }
   }
 
   private KeepMembersPattern() {}
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodAccessPattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodAccessPattern.java
index 7f83937..e0cdff9 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodAccessPattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodAccessPattern.java
@@ -4,13 +4,43 @@
 package com.android.tools.r8.experimental.keepanno.ast;
 
 // TODO: finish this.
-public class KeepMethodAccessPattern {
+public abstract class KeepMethodAccessPattern {
 
   public static KeepMethodAccessPattern any() {
-    return new KeepMethodAccessPattern();
+    return Any.getInstance();
   }
 
-  public boolean isAny() {
-    return true;
+  public abstract boolean isAny();
+
+  private static class Any extends KeepMethodAccessPattern {
+
+    private static Any INSTANCE = null;
+
+    private static Any getInstance() {
+      if (INSTANCE == null) {
+        INSTANCE = new Any();
+      }
+      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 "*";
+    }
   }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodNamePattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodNamePattern.java
index ada6701..032d4a8 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodNamePattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodNamePattern.java
@@ -47,12 +47,28 @@
     public <T> T match(Supplier<T> onAny, Function<String, T> onExact) {
       return onAny.get();
     }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+
+    @Override
+    public String toString() {
+      return "*";
+    }
   }
 
   private static class KeepMethodNameExactPattern extends KeepMethodNamePattern {
     private final String name;
 
     public KeepMethodNameExactPattern(String name) {
+      assert name != null;
       this.name = name;
     }
 
@@ -60,5 +76,27 @@
     public <T> T match(Supplier<T> onAny, Function<String, T> onExact) {
       return onExact.apply(name);
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      KeepMethodNameExactPattern that = (KeepMethodNameExactPattern) o;
+      return name.equals(that.name);
+    }
+
+    @Override
+    public int hashCode() {
+      return name.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return name;
+    }
   }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodParametersPattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodParametersPattern.java
index 4f42202..bd7539d 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodParametersPattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodParametersPattern.java
@@ -22,6 +22,10 @@
 
   public abstract <T> T match(Supplier<T> onAny, Function<List<KeepTypePattern>, T> onList);
 
+  public boolean isAny() {
+    return match(() -> true, params -> false);
+  }
+
   private static class None extends KeepMethodParametersPattern {
     private static None INSTANCE = null;
 
@@ -36,6 +40,21 @@
     public <T> T match(Supplier<T> onAny, Function<List<KeepTypePattern>, T> onList) {
       return onList.apply(Collections.emptyList());
     }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+
+    @Override
+    public String toString() {
+      return "()";
+    }
   }
 
   private static class Any extends KeepMethodParametersPattern {
@@ -52,5 +71,20 @@
     public <T> T match(Supplier<T> onAny, Function<List<KeepTypePattern>, T> onList) {
       return onAny.get();
     }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+
+    @Override
+    public String toString() {
+      return "(...)";
+    }
   }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodPattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodPattern.java
index 5db7d26..b393621 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodPattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodPattern.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.experimental.keepanno.ast;
 
+import java.util.Objects;
+
 public final class KeepMethodPattern extends KeepMemberPattern {
 
   public static Builder builder() {
@@ -62,6 +64,10 @@
       KeepMethodNamePattern namePattern,
       KeepMethodReturnTypePattern returnTypePattern,
       KeepMethodParametersPattern parametersPattern) {
+    assert accessPattern != null;
+    assert namePattern != null;
+    assert returnTypePattern != null;
+    assert parametersPattern != null;
     this.accessPattern = accessPattern;
     this.namePattern = namePattern;
     this.returnTypePattern = returnTypePattern;
@@ -87,4 +93,38 @@
   public KeepMethodParametersPattern getParametersPattern() {
     return parametersPattern;
   }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    KeepMethodPattern that = (KeepMethodPattern) o;
+    return accessPattern.equals(that.accessPattern)
+        && namePattern.equals(that.namePattern)
+        && returnTypePattern.equals(that.returnTypePattern)
+        && parametersPattern.equals(that.parametersPattern);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(accessPattern, namePattern, returnTypePattern, parametersPattern);
+  }
+
+  @Override
+  public String toString() {
+    return "KeepMethodPattern{"
+        + "access="
+        + accessPattern
+        + ", name="
+        + namePattern
+        + ", returnType="
+        + returnTypePattern
+        + ", parameters="
+        + parametersPattern
+        + '}';
+  }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodReturnTypePattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodReturnTypePattern.java
index 4e7b5b9..1926c92 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodReturnTypePattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodReturnTypePattern.java
@@ -23,6 +23,10 @@
 
   public abstract <T> T match(Supplier<T> onVoid, Function<KeepTypePattern, T> onType);
 
+  public boolean isAny() {
+    return match(() -> false, KeepTypePattern::isAny);
+  }
+
   private static class VoidType extends KeepMethodReturnTypePattern {
     private static VoidType INSTANCE = null;
 
@@ -37,6 +41,21 @@
     public <T> T match(Supplier<T> onVoid, Function<KeepTypePattern, T> onType) {
       return onVoid.get();
     }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public int hashCode() {
+      return System.identityHashCode(this);
+    }
+
+    @Override
+    public String toString() {
+      return "void";
+    }
   }
 
   private static class SomeType extends KeepMethodReturnTypePattern {
@@ -44,6 +63,7 @@
     private final KeepTypePattern typePattern;
 
     private SomeType(KeepTypePattern typePattern) {
+      assert typePattern != null;
       this.typePattern = typePattern;
     }
 
@@ -51,5 +71,27 @@
     public <T> T match(Supplier<T> onVoid, Function<KeepTypePattern, T> onType) {
       return onType.apply(typePattern);
     }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+      SomeType someType = (SomeType) o;
+      return typePattern.equals(someType.typePattern);
+    }
+
+    @Override
+    public int hashCode() {
+      return typePattern.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return typePattern.toString();
+    }
   }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepOptions.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepOptions.java
index 539ae71..2def578 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepOptions.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepOptions.java
@@ -7,7 +7,9 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 public final class KeepOptions {
 
@@ -123,4 +125,11 @@
   public int hashCode() {
     return allowedOptions.hashCode();
   }
+
+  @Override
+  public String toString() {
+    return "KeepOptions{"
+        + allowedOptions.stream().map(Objects::toString).collect(Collectors.joining(", "))
+        + '}';
+  }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPackagePattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPackagePattern.java
index 10db385..ae6528f 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPackagePattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPackagePattern.java
@@ -88,6 +88,11 @@
     public int hashCode() {
       return System.identityHashCode(this);
     }
+
+    @Override
+    public String toString() {
+      return "*";
+    }
   }
 
   private static final class KeepPackageTopPattern extends KeepPackageExactPattern {
@@ -114,6 +119,11 @@
     public boolean isTop() {
       return true;
     }
+
+    @Override
+    public String toString() {
+      return "";
+    }
   }
 
   public static class KeepPackageExactPattern extends KeepPackagePattern {
@@ -166,6 +176,11 @@
     public int hashCode() {
       return fullPackage.hashCode();
     }
+
+    @Override
+    public String toString() {
+      return fullPackage;
+    }
   }
 
   public abstract boolean isAny();
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPreconditions.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPreconditions.java
index 2a2c3b2..f9de6ab 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPreconditions.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPreconditions.java
@@ -69,6 +69,11 @@
     public int hashCode() {
       return System.identityHashCode(this);
     }
+
+    @Override
+    public String toString() {
+      return "true";
+    }
   }
 
   private static class KeepPreconditionsSome extends KeepPreconditions {
@@ -107,5 +112,10 @@
     public int hashCode() {
       return preconditions.hashCode();
     }
+
+    @Override
+    public String toString() {
+      return preconditions.toString();
+    }
   }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepQualifiedClassNamePattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepQualifiedClassNamePattern.java
index 8aae535..4423968 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepQualifiedClassNamePattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepQualifiedClassNamePattern.java
@@ -113,4 +113,9 @@
   public int hashCode() {
     return Objects.hash(packagePattern.hashCode(), namePattern.hashCode());
   }
+
+  @Override
+  public String toString() {
+    return packagePattern + (packagePattern.isTop() ? "" : ".") + namePattern;
+  }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTarget.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTarget.java
index 059668a..3a318c3 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTarget.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTarget.java
@@ -74,4 +74,9 @@
   public int hashCode() {
     return Objects.hash(item, options);
   }
+
+  @Override
+  public String toString() {
+    return "KeepTarget{" + "item=" + item + ", options=" + options + '}';
+  }
 }
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTypePattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTypePattern.java
index eff9e9c..b105558 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTypePattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTypePattern.java
@@ -23,6 +23,21 @@
     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 abstract boolean isAny();
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepUnqualfiedClassNamePattern.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepUnqualfiedClassNamePattern.java
index ab38ed2..116ccf9 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepUnqualfiedClassNamePattern.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepUnqualfiedClassNamePattern.java
@@ -71,6 +71,11 @@
     public int hashCode() {
       return System.identityHashCode(this);
     }
+
+    @Override
+    public String toString() {
+      return "*";
+    }
   }
 
   public static class KeepClassNameExactPattern extends KeepUnqualfiedClassNamePattern {
@@ -117,6 +122,11 @@
     public int hashCode() {
       return className.hashCode();
     }
+
+    @Override
+    public String toString() {
+      return className;
+    }
   }
 
   public abstract boolean isAny();
diff --git a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/processor/KeepEdgeProcessor.java b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/processor/KeepEdgeProcessor.java
index 6eb851d..2140cf6 100644
--- a/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/processor/KeepEdgeProcessor.java
+++ b/src/keepanno/main/java/com/android/tools/r8/experimental/keepanno/processor/KeepEdgeProcessor.java
@@ -8,12 +8,17 @@
 import static org.objectweb.asm.Opcodes.ACC_SUPER;
 
 import com.android.tools.r8.experimental.keepanno.annotations.KeepConstants;
+import com.android.tools.r8.experimental.keepanno.annotations.KeepConstants.Edge;
+import com.android.tools.r8.experimental.keepanno.annotations.KeepConstants.Target;
 import com.android.tools.r8.experimental.keepanno.asm.KeepEdgeReader;
 import com.android.tools.r8.experimental.keepanno.asm.KeepEdgeWriter;
 import com.android.tools.r8.experimental.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.experimental.keepanno.ast.KeepEdge;
 import com.android.tools.r8.experimental.keepanno.ast.KeepEdge.Builder;
 import com.android.tools.r8.experimental.keepanno.ast.KeepItemPattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMembersPattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.experimental.keepanno.ast.KeepQualifiedClassNamePattern;
 import com.android.tools.r8.experimental.keepanno.ast.KeepTarget;
 import com.android.tools.r8.experimental.keepanno.utils.Unimplemented;
@@ -98,7 +103,7 @@
   }
 
   private void processPreconditions(Builder edgeBuilder, AnnotationMirror mirror) {
-    AnnotationValue preconditions = getAnnotationValue(mirror, "preconditions");
+    AnnotationValue preconditions = getAnnotationValue(mirror, Edge.preconditions);
     if (preconditions == null) {
       return;
     }
@@ -106,7 +111,7 @@
   }
 
   private void processConsequences(Builder edgeBuilder, AnnotationMirror mirror) {
-    AnnotationValue consequences = getAnnotationValue(mirror, "consequences");
+    AnnotationValue consequences = getAnnotationValue(mirror, Edge.consequences);
     if (consequences == null) {
       return;
     }
@@ -123,11 +128,23 @@
 
   private void processTarget(KeepTarget.Builder builder, AnnotationMirror mirror) {
     KeepItemPattern.Builder itemBuilder = KeepItemPattern.builder();
-    AnnotationValue classConstantValue = getAnnotationValue(mirror, "classConstant");
+    AnnotationValue classConstantValue = getAnnotationValue(mirror, Target.classConstant);
     if (classConstantValue != null) {
       DeclaredType type = AnnotationClassValueVisitor.getType(classConstantValue);
       itemBuilder.setClassPattern(KeepQualifiedClassNamePattern.exact(type.toString()));
     }
+    AnnotationValue methodNameValue = getAnnotationValue(mirror, Target.methodName);
+    if (methodNameValue != null) {
+      String methodName = AnnotationStringValueVisitor.getString(methodNameValue);
+      itemBuilder.setMembersPattern(
+          KeepMembersPattern.builder()
+              .addMethodPattern(
+                  KeepMethodPattern.builder()
+                      .setNamePattern(KeepMethodNamePattern.exact(methodName))
+                      .build())
+              .build());
+    }
+
     builder.setItem(itemBuilder.build());
   }
 
@@ -211,6 +228,21 @@
     }
   }
 
+  private static class AnnotationStringValueVisitor
+      extends AnnotationValueVisitorBase<AnnotationStringValueVisitor> {
+    private String string;
+
+    public static String getString(AnnotationValue value) {
+      return new AnnotationStringValueVisitor().onValue(value).string;
+    }
+
+    @Override
+    public AnnotationStringValueVisitor visitString(String string, Object ignore) {
+      this.string = string;
+      return this;
+    }
+  }
+
   private static class AnnotationClassValueVisitor
       extends AnnotationValueVisitorBase<AnnotationClassValueVisitor> {
     private DeclaredType type = null;
diff --git a/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepClassAndDefaultConstructorSource.java b/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepClassAndDefaultConstructorSource.java
index 0550498..965651d 100644
--- a/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepClassAndDefaultConstructorSource.java
+++ b/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepClassAndDefaultConstructorSource.java
@@ -9,13 +9,9 @@
 @KeepEdge(
     consequences = {
       // Keep the class to allow lookup of it.
-      @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.class)
-      // TODO(b/248408342): Add default constructor target.
-      // // Keep the default constructor.
-      // @KeepTarget(
-      //     classConstant = KeepClassAndDefaultConstructorSource.class,
-      //     methodName = "<init>"
-      // )
+      @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.class),
+      // Keep the default constructor.
+      @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.class, methodName = "<init>")
     })
 public class KeepClassAndDefaultConstructorSource {
 
diff --git a/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepSourceEdges.java b/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepSourceEdges.java
index 6c4f8fa..a334992 100644
--- a/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepSourceEdges.java
+++ b/src/test/java/com/android/tools/r8/experimental/keepanno/testsource/KeepSourceEdges.java
@@ -6,6 +6,9 @@
 import com.android.tools.r8.experimental.keepanno.ast.KeepConsequences;
 import com.android.tools.r8.experimental.keepanno.ast.KeepEdge;
 import com.android.tools.r8.experimental.keepanno.ast.KeepItemPattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMembersPattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.experimental.keepanno.ast.KeepMethodPattern;
 import com.android.tools.r8.experimental.keepanno.ast.KeepQualifiedClassNamePattern;
 import com.android.tools.r8.experimental.keepanno.ast.KeepTarget;
 import java.util.Collections;
@@ -27,10 +30,24 @@
 
   public static Set<KeepEdge> getKeepClassAndDefaultConstructorSourceEdges() {
     Class<?> clazz = KeepClassAndDefaultConstructorSource.class;
+    // Build the class target.
     KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName());
-    KeepItemPattern item = KeepItemPattern.builder().setClassPattern(name).build();
-    KeepTarget target = KeepTarget.builder().setItem(item).build();
-    KeepConsequences consequences = KeepConsequences.builder().addTarget(target).build();
+    KeepItemPattern classItem = KeepItemPattern.builder().setClassPattern(name).build();
+    KeepTarget classTarget = KeepTarget.builder().setItem(classItem).build();
+    // Build the constructor target.
+    KeepMethodPattern constructorMethod =
+        KeepMethodPattern.builder().setNamePattern(KeepMethodNamePattern.exact("<init>")).build();
+    KeepMembersPattern constructorMembers =
+        KeepMembersPattern.builder().addMethodPattern(constructorMethod).build();
+    KeepItemPattern constructorItem =
+        KeepItemPattern.builder()
+            .setClassPattern(name)
+            .setMembersPattern(constructorMembers)
+            .build();
+    KeepTarget constructorTarget = KeepTarget.builder().setItem(constructorItem).build();
+    // The consequet set is the class an its constructor.
+    KeepConsequences consequences =
+        KeepConsequences.builder().addTarget(classTarget).addTarget(constructorTarget).build();
     KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
     return Collections.singleton(edge);
   }