[Retrace] Separate regular expression retracer to new setup

Bug: 169346455
Change-Id: Ice4a7a49461abbc1553863245e9483b142f74134
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index d9dd386..bd537a6 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl;
 import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl.RetraceStackTraceProxyImpl;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
+import com.android.tools.r8.retrace.internal.StackTraceVisitor;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
 import com.android.tools.r8.utils.OptionsParsing;
@@ -173,62 +174,53 @@
       timing.end();
       RetraceCommandLineResult result;
       timing.begin("Parse and Retrace");
-      if (command.regularExpression != null) {
-        result =
-            new RetraceRegularExpression(
-                    retracer,
-                    command.stackTrace,
-                    command.diagnosticsHandler,
-                    command.regularExpression,
-                    command.isVerbose)
-                .retrace();
-      } else {
-        PlainStackTraceVisitor plainStackTraceVisitor =
-            new PlainStackTraceVisitor(command.stackTrace, command.diagnosticsHandler);
+      StackTraceVisitor<StackTraceElementStringProxy> stackTraceVisitor =
+          command.regularExpression != null
+              ? new RetraceRegularExpression(
+                  retracer, command.stackTrace, command.regularExpression)
+              : new PlainStackTraceVisitor(command.stackTrace, command.diagnosticsHandler);
         StackTraceElementProxyRetracer<StackTraceElementStringProxy> proxyRetracer =
             new StackTraceElementProxyRetracerImpl<>(retracer);
         List<String> retracedStrings = new ArrayList<>();
-        plainStackTraceVisitor.forEach(
-            stackTraceElement -> {
-              Box<List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>> currentList =
-                  new Box<>();
-              Map<
-                      RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
-                      List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>>
-                  ambiguousBlocks = new HashMap<>();
-              proxyRetracer
-                  .retrace(stackTraceElement)
-                  .forEach(
-                      retracedElement -> {
-                        if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
-                          List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>> block =
-                              new ArrayList<>();
-                          ambiguousBlocks.put(retracedElement, block);
-                          currentList.set(block);
-                        }
-                        currentList.get().add(retracedElement);
-                      });
-              ambiguousBlocks.keySet().stream()
-                  .sorted()
-                  .forEach(
-                      topFrame -> {
-                        ambiguousBlocks
-                            .get(topFrame)
-                            .forEach(
-                                frame -> {
-                                  StackTraceElementStringProxy originalItem =
-                                      frame.getOriginalItem();
-                                  retracedStrings.add(
-                                      originalItem.toRetracedItem(
-                                          frame, !currentList.isSet(), command.isVerbose));
-                                  // Use the current list as indicator for us seeing the first
-                                  // sorted element.
-                                  currentList.set(null);
-                                });
-                      });
-            });
+      stackTraceVisitor.forEach(
+          stackTraceElement -> {
+            Box<List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>> currentList =
+                new Box<>();
+            Map<
+                    RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
+                    List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>>
+                ambiguousBlocks = new HashMap<>();
+            proxyRetracer
+                .retrace(stackTraceElement)
+                .forEach(
+                    retracedElement -> {
+                      if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
+                        List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>> block =
+                            new ArrayList<>();
+                        ambiguousBlocks.put(retracedElement, block);
+                        currentList.set(block);
+                      }
+                      currentList.get().add(retracedElement);
+                    });
+            ambiguousBlocks.keySet().stream()
+                .sorted()
+                .forEach(
+                    topFrame -> {
+                      ambiguousBlocks
+                          .get(topFrame)
+                          .forEach(
+                              frame -> {
+                                StackTraceElementStringProxy originalItem = frame.getOriginalItem();
+                                retracedStrings.add(
+                                    originalItem.toRetracedItem(
+                                        frame, !currentList.isSet(), command.isVerbose));
+                                // Use the current list as indicator for us seeing the first
+                                // sorted element.
+                                currentList.set(null);
+                              });
+                    });
+          });
         result = new RetraceCommandLineResult(retracedStrings);
-      }
       timing.end();
       timing.begin("Report result");
       command.retracedStackTraceConsumer.accept(result.getNodes());
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
index f141080..d3b6014 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceFieldResult.java
@@ -27,5 +27,7 @@
     RetraceFieldResult getRetraceFieldResult();
 
     RetraceClassResult.Element getClassElement();
+
+    RetraceSourceFileResult retraceSourceFile(String sourceFile);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
index ebae15c..d9d1375 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceProxy.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.retrace;
 
 import com.android.tools.r8.Keep;
+import java.util.List;
 
 @Keep
 public interface RetraceStackTraceProxy<T extends StackTraceElementProxy<?>>
@@ -18,16 +19,28 @@
 
   boolean hasRetracedMethod();
 
+  boolean hasRetracedField();
+
   boolean hasSourceFile();
 
   boolean hasLineNumber();
 
+  boolean hasFieldOrReturnType();
+
+  boolean hasMethodArguments();
+
   T getOriginalItem();
 
   RetracedClass getRetracedClass();
 
   RetracedMethod getRetracedMethod();
 
+  RetracedField getRetracedField();
+
+  RetracedType getRetracedFieldOrReturnType();
+
+  List<RetracedType> getMethodArguments();
+
   String getSourceFile();
 
   int getLineNumber();
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
index b9fa2d3..2c0c1fa 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceTypeResult.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.retrace;
 
 import com.android.tools.r8.Keep;
-import com.android.tools.r8.retrace.internal.RetracedTypeImpl;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
@@ -21,6 +20,6 @@
   @Keep
   interface Element {
 
-    RetracedTypeImpl getType();
+    RetracedType getType();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java b/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java
index 10aa77c..f28b87e 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedClassMember.java
@@ -5,10 +5,9 @@
 package com.android.tools.r8.retrace;
 
 import com.android.tools.r8.Keep;
-import com.android.tools.r8.retrace.internal.RetracedClassImpl;
 
 @Keep
 public interface RetracedClassMember {
 
-  RetracedClassImpl getHolderClass();
+  RetracedClass getHolderClass();
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
index efaab05..09c1a8f 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxy.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.retrace;
 
 import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
 
 @Keep
 public abstract class StackTraceElementProxy<T> {
@@ -17,11 +18,23 @@
 
   public abstract boolean hasLineNumber();
 
-  public abstract String className();
+  public abstract boolean hasFieldName();
 
-  public abstract String methodName();
+  public abstract boolean hasFieldOrReturnType();
 
-  public abstract String fileName();
+  public abstract boolean hasMethodArguments();
 
-  public abstract int lineNumber();
+  public abstract ClassReference getClassReference();
+
+  public abstract String getMethodName();
+
+  public abstract String getFileName();
+
+  public abstract int getLineNumber();
+
+  public abstract String getFieldName();
+
+  public abstract String getFieldOrReturnType();
+
+  public abstract String getMethodArguments();
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java b/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java
index 4a3b93c..27d9a54 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/FieldDefinition.java
@@ -43,7 +43,7 @@
 
     @Override
     FieldDefinition substituteHolder(ClassReference newHolder) {
-      return FieldDefinition.create(classReference, name);
+      return FieldDefinition.create(newHolder, name);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
index 0aac05f..7f7d7fc 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
@@ -7,6 +7,7 @@
 import static com.google.common.base.Predicates.not;
 
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
 import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
 import com.android.tools.r8.utils.DescriptorUtils;
 import java.util.List;
@@ -92,7 +93,7 @@
         return null;
       }
       return StackTraceElementStringProxy.builder(line)
-          .registerClassName(exceptionStartIndex, messageStartIndex)
+          .registerClassName(exceptionStartIndex, messageStartIndex, ClassNameType.TYPENAME)
           .build();
     }
   }
@@ -161,7 +162,7 @@
       }
       StackTraceElementStringProxyBuilder builder =
           StackTraceElementStringProxy.builder(line)
-              .registerClassName(classStartIndex, methodSeparator)
+              .registerClassName(classStartIndex, methodSeparator, ClassNameType.TYPENAME)
               .registerMethodName(methodSeparator + 1, parensStart);
       // Check if we have a filename and position.
       int separatorIndex = firstCharFromIndex(line, parensStart, ':');
@@ -201,7 +202,7 @@
         return null;
       }
       return StackTraceElementStringProxy.builder(line)
-          .registerClassName(exceptionStartIndex, lastBracketPosition)
+          .registerClassName(exceptionStartIndex, lastBracketPosition, ClassNameType.TYPENAME)
           .build();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index 066b979..3a65b0a 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.retrace.RetraceFieldResult;
-import com.android.tools.r8.retrace.Retracer;
+import com.android.tools.r8.retrace.RetraceSourceFileResult;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Pair;
 import java.util.List;
@@ -22,13 +22,13 @@
   private final RetraceClassResultImpl classResult;
   private final List<Pair<RetraceClassResultImpl.ElementImpl, List<MemberNaming>>> memberNamings;
   private final FieldDefinition fieldDefinition;
-  private final Retracer retracer;
+  private final RetracerImpl retracer;
 
   RetraceFieldResultImpl(
       RetraceClassResultImpl classResult,
       List<Pair<RetraceClassResultImpl.ElementImpl, List<MemberNaming>>> memberNamings,
       FieldDefinition fieldDefinition,
-      Retracer retracer) {
+      RetracerImpl retracer) {
     this.classResult = classResult;
     this.memberNamings = memberNamings;
     this.fieldDefinition = fieldDefinition;
@@ -131,5 +131,11 @@
     public RetraceClassResultImpl.ElementImpl getClassElement() {
       return classElement;
     }
+
+    @Override
+    public RetraceSourceFileResult retraceSourceFile(String sourceFile) {
+      return RetraceUtils.getSourceFile(
+          classElement, fieldReference.getHolderClass(), sourceFile, retraceFieldResult.retracer);
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
index 6ba4d1a..ca9a2f8 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
@@ -4,42 +4,19 @@
 
 package com.android.tools.r8.retrace.internal;
 
-import static com.android.tools.r8.retrace.internal.RetraceUtils.methodDescriptionFromRetraceMethod;
-
-import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.retrace.RetraceClassResult;
-import com.android.tools.r8.retrace.RetraceFieldResult;
-import com.android.tools.r8.retrace.RetraceFrameResult;
-import com.android.tools.r8.retrace.RetraceFrameResult.Element;
-import com.android.tools.r8.retrace.RetraceSourceFileResult;
-import com.android.tools.r8.retrace.RetracedClass;
-import com.android.tools.r8.retrace.RetracedField;
-import com.android.tools.r8.retrace.RetracedField.KnownRetracedField;
-import com.android.tools.r8.retrace.RetracedMethod;
-import com.android.tools.r8.retrace.internal.RetraceRegularExpression.ClassNameGroup.ClassNameGroupHandler;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.Lists;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
-import java.util.Objects;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
+import java.util.function.Consumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 
-public class RetraceRegularExpression {
+public class RetraceRegularExpression implements StackTraceVisitor<StackTraceElementStringProxy> {
 
   private final RetracerImpl retracer;
   private final List<String> stackTrace;
-  private final DiagnosticsHandler diagnosticsHandler;
   private final String regularExpression;
 
   private static final int NO_MATCH = -1;
@@ -52,28 +29,21 @@
   private final LineNumberGroup lineNumberGroup = new LineNumberGroup();
   private final FieldOrReturnTypeGroup fieldOrReturnTypeGroup = new FieldOrReturnTypeGroup();
   private final MethodArgumentsGroup methodArgumentsGroup = new MethodArgumentsGroup();
-
-  private final MethodNameGroup methodNameGroup;
-  private final FieldNameGroup fieldNameGroup;
+  private final MethodNameGroup methodNameGroup = new MethodNameGroup();
+  private final FieldNameGroup fieldNameGroup = new FieldNameGroup();
 
   private static final String CAPTURE_GROUP_PREFIX = "captureGroup";
   private static final int FIRST_CAPTURE_GROUP_INDEX = 0;
 
   public RetraceRegularExpression(
-      RetracerImpl retracer,
-      List<String> stackTrace,
-      DiagnosticsHandler diagnosticsHandler,
-      String regularExpression,
-      boolean isVerbose) {
+      RetracerImpl retracer, List<String> stackTrace, String regularExpression) {
     this.retracer = retracer;
     this.stackTrace = stackTrace;
-    this.diagnosticsHandler = diagnosticsHandler;
     this.regularExpression = regularExpression;
-    methodNameGroup = new MethodNameGroup(isVerbose);
-    fieldNameGroup = new FieldNameGroup(isVerbose);
   }
 
-  public RetraceCommandLineResult retrace() {
+  @Override
+  public void forEach(Consumer<StackTraceElementStringProxy> consumer) {
     List<RegularExpressionGroupHandler> handlers = new ArrayList<>();
     StringBuilder refinedRegularExpressionBuilder = new StringBuilder();
     registerGroups(
@@ -83,74 +53,22 @@
         FIRST_CAPTURE_GROUP_INDEX);
     String refinedRegularExpression = refinedRegularExpressionBuilder.toString();
     Pattern compiledPattern = Pattern.compile(refinedRegularExpression);
-    List<String> result = new ArrayList<>();
     for (String string : stackTrace) {
+      StackTraceElementStringProxyBuilder proxyBuilder =
+          StackTraceElementStringProxy.builder(string);
       Matcher matcher = compiledPattern.matcher(string);
-      if (!matcher.matches()) {
-        result.add(string);
-        continue;
-      }
-      // Iterate through handlers to set contexts. That will allow us to process all handlers from
-      // left to right.
-      RetraceStringContext initialContext = RetraceStringContext.empty();
-      for (RegularExpressionGroupHandler handler : handlers) {
-        initialContext = handler.buildInitial(initialContext, matcher, retracer);
-      }
-      final RetraceString initialRetraceString = RetraceString.start(initialContext);
-      List<RetraceString> retracedStrings = Lists.newArrayList(initialRetraceString);
-      for (RegularExpressionGroupHandler handler : handlers) {
-        retracedStrings = handler.handleMatch(string, retracedStrings, matcher, retracer);
-      }
-      if (retracedStrings.isEmpty()) {
-        // We could not find a match. Output the identity.
-        result.add(string);
-      }
-      boolean isAmbiguous = retracedStrings.size() > 1 && retracedStrings.get(0).isAmbiguous();
-      if (isAmbiguous) {
-        retracedStrings.sort(new RetraceLineComparator());
-      }
-      RetracedClass previousContext = null;
-      for (RetraceString retracedString : retracedStrings) {
-        String finalString = retracedString.builder.build(string);
-        if (!isAmbiguous) {
-          result.add(finalString);
-          continue;
+      if (matcher.matches()) {
+        boolean seenMatchedClassHandler = false;
+        for (RegularExpressionGroupHandler handler : handlers) {
+          if (seenMatchedClassHandler && handler.isClassHandler()) {
+            continue;
+          }
+          if (handler.matchHandler(proxyBuilder, matcher)) {
+            seenMatchedClassHandler |= handler.isClassHandler();
+          }
         }
-        assert retracedString.getClassContext() != null;
-        RetracedClass currentContext = retracedString.getClassContext().getRetracedClass();
-        if (currentContext.equals(previousContext)) {
-          int firstNonWhitespaceCharacter = StringUtils.firstNonWhitespaceCharacter(finalString);
-          finalString =
-              finalString.substring(0, firstNonWhitespaceCharacter)
-                  + "<OR> "
-                  + finalString.substring(firstNonWhitespaceCharacter);
-        }
-        previousContext = currentContext;
-        result.add(finalString);
       }
-    }
-    return new RetraceCommandLineResult(result);
-  }
-
-  static class RetraceLineComparator extends AmbiguousComparator<RetraceString> {
-
-    RetraceLineComparator() {
-      super(
-          (line, t) -> {
-            switch (t) {
-              case CLASS:
-                return line.getClassContext().getRetracedClass().getTypeName();
-              case METHOD:
-                return line.getMethodContext().getTopFrame().getMethodName();
-              case SOURCE:
-                return line.getSource();
-              case LINE:
-                return line.getLineNumber() + "";
-              default:
-                assert false;
-            }
-            throw new RuntimeException("Comparator key is unknown");
-          });
+      consumer.accept(proxyBuilder.build());
     }
   }
 
@@ -160,7 +78,6 @@
       List<RegularExpressionGroupHandler> handlers,
       int captureGroupIndex) {
     int lastCommittedIndex = 0;
-    RegularExpressionGroupHandler lastHandler = null;
     boolean seenPercentage = false;
     boolean escaped = false;
     for (int i = 0; i < regularExpression.length(); i++) {
@@ -180,23 +97,7 @@
               .append(">")
               .append(group.subExpression())
               .append(")");
-          final RegularExpressionGroupHandler handler = group.createHandler(captureGroupName);
-          // If we see a pattern as %c.%m or %C/%m, then register the groups to allow delaying
-          // writing of the class string until we have the fully qualified member.
-          if (lastHandler != null
-              && handler.isQualifiedHandler()
-              && lastHandler.isClassNameGroupHandler()
-              && lastCommittedIndex == i - 3
-              && isTypeOrBinarySeparator(regularExpression, lastCommittedIndex, i - 2)) {
-            final ClassNameGroupHandler classNameGroupHandler =
-                lastHandler.asClassNameGroupHandler();
-            final QualifiedRegularExpressionGroupHandler qualifiedHandler =
-                handler.asQualifiedHandler();
-            classNameGroupHandler.setQualifiedHandler(qualifiedHandler);
-            qualifiedHandler.setClassNameGroupHandler(classNameGroupHandler);
-          }
-          lastHandler = handler;
-          handlers.add(handler);
+          handlers.add(group.createHandler(captureGroupName));
         }
         lastCommittedIndex = i + 1;
         seenPercentage = false;
@@ -247,250 +148,13 @@
     }
   }
 
-  static class RetraceStringContext {
-    private final RetraceClassResult.Element classContext;
-    private final RetraceFrameResult.Element methodContext;
-    private final RetracedClass qualifiedClassContext;
-    private final RetracedMethod qualifiedMethodContext;
-    private final String methodName;
-    private final int minifiedLineNumber;
-    private final int originalLineNumber;
-    private final String source;
-    private final boolean isAmbiguous;
-
-    private RetraceStringContext(
-        RetraceClassResult.Element classContext,
-        RetraceFrameResult.Element methodContext,
-        RetracedClass qualifiedClassContext,
-        RetracedMethod qualifiedMethodContext,
-        String methodName,
-        int minifiedLineNumber,
-        int originalLineNumber,
-        String source,
-        boolean isAmbiguous) {
-      this.classContext = classContext;
-      this.methodContext = methodContext;
-      this.qualifiedClassContext = qualifiedClassContext;
-      this.qualifiedMethodContext = qualifiedMethodContext;
-      this.methodName = methodName;
-      this.minifiedLineNumber = minifiedLineNumber;
-      this.originalLineNumber = originalLineNumber;
-      this.source = source;
-      this.isAmbiguous = isAmbiguous;
-    }
-
-    private static RetraceStringContext empty() {
-      return new RetraceStringContext(
-          null, null, null, null, null, NO_MATCH, NO_MATCH, null, false);
-    }
-
-    private RetraceStringContext withClassContext(
-        RetraceClassResult.Element classContext, RetracedClass qualifiedContext) {
-      return new RetraceStringContext(
-          classContext,
-          methodContext,
-          qualifiedContext,
-          qualifiedMethodContext,
-          methodName,
-          minifiedLineNumber,
-          originalLineNumber,
-          source,
-          isAmbiguous);
-    }
-
-    private RetraceStringContext withMethodName(String methodName) {
-      return new RetraceStringContext(
-          classContext,
-          methodContext,
-          qualifiedClassContext,
-          qualifiedMethodContext,
-          methodName,
-          minifiedLineNumber,
-          originalLineNumber,
-          source,
-          isAmbiguous);
-    }
-
-    private RetraceStringContext withMethodContext(Element methodContext, boolean isAmbiguous) {
-      return new RetraceStringContext(
-          classContext,
-          methodContext,
-          qualifiedClassContext,
-          qualifiedMethodContext,
-          methodName,
-          minifiedLineNumber,
-          originalLineNumber,
-          source,
-          isAmbiguous);
-    }
-
-    private RetraceStringContext withQualifiedClassContext(RetracedClass qualifiedContext) {
-      return new RetraceStringContext(
-          classContext,
-          methodContext,
-          qualifiedContext,
-          qualifiedMethodContext,
-          methodName,
-          minifiedLineNumber,
-          originalLineNumber,
-          source,
-          isAmbiguous);
-    }
-
-    private RetraceStringContext withQualifiedMethodContext(RetracedMethod qualifiedContext) {
-      return new RetraceStringContext(
-          classContext,
-          methodContext,
-          qualifiedContext.getHolderClass(),
-          qualifiedContext,
-          methodName,
-          minifiedLineNumber,
-          originalLineNumber,
-          source,
-          isAmbiguous);
-    }
-
-    public RetraceStringContext withSource(String source) {
-      return new RetraceStringContext(
-          classContext,
-          methodContext,
-          qualifiedClassContext,
-          qualifiedMethodContext,
-          methodName,
-          minifiedLineNumber,
-          originalLineNumber,
-          source,
-          isAmbiguous);
-    }
-
-    public RetraceStringContext withLineNumbers(int minifiedLineNumber, int originalLineNumber) {
-      return new RetraceStringContext(
-          classContext,
-          methodContext,
-          qualifiedClassContext,
-          qualifiedMethodContext,
-          methodName,
-          minifiedLineNumber,
-          originalLineNumber,
-          source,
-          isAmbiguous);
-    }
-  }
-
-  static class RetraceStringBuilder {
-
-    private final StringBuilder retracedString;
-    private int lastCommittedIndex;
-
-    private RetraceStringBuilder(String retracedString, int lastCommittedIndex) {
-      this.retracedString = new StringBuilder(retracedString);
-      this.lastCommittedIndex = lastCommittedIndex;
-    }
-
-    private void appendRetracedString(
-        String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
-      retracedString.append(source, lastCommittedIndex, originalFromIndex);
-      retracedString.append(stringToAppend);
-      lastCommittedIndex = originalToIndex;
-    }
-
-    private String build(String source) {
-      return retracedString.append(source, lastCommittedIndex, source.length()).toString();
-    }
-  }
-
-  private static class RetraceString {
-
-    private final RetraceStringBuilder builder;
-    private final RetraceStringContext context;
-
-    private RetraceString(RetraceStringBuilder builder, RetraceStringContext context) {
-      this.builder = builder;
-      this.context = context;
-    }
-
-    private RetraceClassResult.Element getClassContext() {
-      return context.classContext;
-    }
-
-    private RetraceFrameResult.Element getMethodContext() {
-      return context.methodContext;
-    }
-
-    private String getSource() {
-      return context.source;
-    }
-
-    private int getLineNumber() {
-      return context.originalLineNumber;
-    }
-
-    private boolean isAmbiguous() {
-      return context.isAmbiguous;
-    }
-
-    private static RetraceString start(RetraceStringContext initialContext) {
-      return new RetraceString(new RetraceStringBuilder("", 0), initialContext);
-    }
-
-    private RetraceString updateContext(
-        Function<RetraceStringContext, RetraceStringContext> update) {
-      return new RetraceString(builder, update.apply(context));
-    }
-
-    private RetraceString duplicate(RetraceStringContext newContext) {
-      return new RetraceString(
-          new RetraceStringBuilder(builder.retracedString.toString(), builder.lastCommittedIndex),
-          newContext);
-    }
-
-    private RetraceString appendRetracedString(
-        String source, String stringToAppend, int originalFromIndex, int originalToIndex) {
-      builder.appendRetracedString(source, stringToAppend, originalFromIndex, originalToIndex);
-      return this;
-    }
-  }
-
   private interface RegularExpressionGroupHandler {
 
-    List<RetraceString> handleMatch(
-        String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer);
+    boolean matchHandler(StackTraceElementStringProxyBuilder builder, Matcher matcher);
 
-    default RetraceStringContext buildInitial(
-        RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
-      return context;
-    }
-
-    default boolean isClassNameGroupHandler() {
+    default boolean isClassHandler() {
       return false;
     }
-
-    default ClassNameGroupHandler asClassNameGroupHandler() {
-      return null;
-    }
-
-    default boolean isQualifiedHandler() {
-      return false;
-    }
-
-    default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
-      return null;
-    }
-  }
-
-  private interface QualifiedRegularExpressionGroupHandler extends RegularExpressionGroupHandler {
-
-    @Override
-    default boolean isQualifiedHandler() {
-      return true;
-    }
-
-    @Override
-    default QualifiedRegularExpressionGroupHandler asQualifiedHandler() {
-      return this;
-    }
-
-    void setClassNameGroupHandler(ClassNameGroupHandler handler);
   }
 
   private abstract static class RegularExpressionGroup {
@@ -513,107 +177,31 @@
 
   abstract static class ClassNameGroup extends RegularExpressionGroup {
 
-    abstract String getClassName(RetracedClass classReference);
-
-    abstract ClassReference classFromMatch(String match);
+    abstract ClassNameType getClassNameType();
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return new ClassNameGroupHandler(this, captureGroup);
-    }
-
-    static class ClassNameGroupHandler implements RegularExpressionGroupHandler {
-
-      private RetraceClassResultImpl retraceClassResult = null;
-      private final ClassNameGroup classNameGroup;
-      private final String captureGroup;
-      private RegularExpressionGroupHandler qualifiedHandler;
-
-      public ClassNameGroupHandler(ClassNameGroup classNameGroup, String captureGroup) {
-        this.classNameGroup = classNameGroup;
-        this.captureGroup = captureGroup;
-      }
-
-      @Override
-      public List<RetraceString> handleMatch(
-          String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
-        final int startOfGroup = matcher.start(captureGroup);
-        if (startOfGroup == NO_MATCH) {
-          return strings;
+      return new RegularExpressionGroupHandler() {
+        @Override
+        public boolean matchHandler(StackTraceElementStringProxyBuilder builder, Matcher matcher) {
+          int startOfGroup = matcher.start(captureGroup);
+          if (startOfGroup == NO_MATCH) {
+            return false;
+          }
+          String typeName = matcher.group(captureGroup);
+          if (typeName.equals("Suppressed")) {
+            // Ensure we do not map supressed.
+            return false;
+          }
+          builder.registerClassName(startOfGroup, matcher.end(captureGroup), getClassNameType());
+          return true;
         }
-        String typeName = matcher.group(captureGroup);
-        RetraceClassResultImpl retraceResult =
-            retraceClassResult == null
-                ? retracer.retraceClass(classNameGroup.classFromMatch(typeName))
-                : retraceClassResult;
-        assert !retraceResult.isAmbiguous();
-        List<RetraceString> retraceStrings = new ArrayList<>(strings.size());
-        for (RetraceString retraceString : strings) {
-          retraceResult.forEach(
-              element -> {
-                RetraceString newRetraceString =
-                    retraceString.updateContext(
-                        context -> context.withClassContext(element, element.getRetracedClass()));
-                retraceStrings.add(newRetraceString);
-                if (qualifiedHandler == null) {
-                  // If there is no qualified handler, commit right away.
-                  newRetraceString.builder.appendRetracedString(
-                      original,
-                      classNameGroup.getClassName(element.getRetracedClass()),
-                      startOfGroup,
-                      matcher.end(captureGroup));
-                }
-              });
+
+        @Override
+        public boolean isClassHandler() {
+          return true;
         }
-        return retraceStrings;
-      }
-
-      void commitClassName(
-          String original,
-          RetraceString retraceString,
-          RetracedClass qualifiedContext,
-          Matcher matcher) {
-        if (matcher.start(captureGroup) == NO_MATCH) {
-          return;
-        }
-        retraceString.builder.appendRetracedString(
-            original,
-            classNameGroup.getClassName(qualifiedContext),
-            matcher.start(captureGroup),
-            matcher.end(captureGroup));
-      }
-
-      @Override
-      public RetraceStringContext buildInitial(
-          RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
-        // Reset the local class context since this the same handler is used for multiple lines.
-        retraceClassResult = null;
-        if (matcher.start(captureGroup) == NO_MATCH || context.classContext != null) {
-          return context;
-        }
-        String typeName = matcher.group(captureGroup);
-        retraceClassResult = retracer.retraceClass(classNameGroup.classFromMatch(typeName));
-        assert !retraceClassResult.isAmbiguous();
-        Box<RetraceStringContext> box = new Box<>();
-        retraceClassResult.forEach(
-            element -> box.set(context.withClassContext(element, element.getRetracedClass())));
-        return box.get();
-      }
-
-      public void setQualifiedHandler(RegularExpressionGroupHandler handler) {
-        assert handler.isQualifiedHandler();
-        this.qualifiedHandler = handler;
-      }
-
-      @Override
-      public boolean isClassNameGroupHandler() {
-        return true;
-      }
-
-      @Override
-      public ClassNameGroupHandler asClassNameGroupHandler() {
-        return this;
-      }
+      };
     }
   }
 
@@ -625,13 +213,8 @@
     }
 
     @Override
-    String getClassName(RetracedClass classReference) {
-      return classReference.getTypeName();
-    }
-
-    @Override
-    ClassReference classFromMatch(String match) {
-      return Reference.classFromTypeName(match);
+    ClassNameType getClassNameType() {
+      return ClassNameType.TYPENAME;
     }
   }
 
@@ -643,24 +226,13 @@
     }
 
     @Override
-    String getClassName(RetracedClass classReference) {
-      return classReference.getBinaryName();
-    }
-
-    @Override
-    ClassReference classFromMatch(String match) {
-      return Reference.classFromBinaryName(match);
+    ClassNameType getClassNameType() {
+      return ClassNameType.BINARY;
     }
   }
 
   private static class MethodNameGroup extends RegularExpressionGroup {
 
-    private final boolean printVerbose;
-
-    public MethodNameGroup(boolean printVerbose) {
-      this.printVerbose = printVerbose;
-    }
-
     @Override
     String subExpression() {
       return METHOD_NAME_REGULAR_EXPRESSION;
@@ -668,98 +240,19 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return new QualifiedRegularExpressionGroupHandler() {
-
-        private ClassNameGroupHandler classNameGroupHandler;
-
-        @Override
-        public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
-          classNameGroupHandler = handler;
+      return (builder, matcher) -> {
+        int startOfGroup = matcher.start(captureGroup);
+        if (startOfGroup == NO_MATCH) {
+          return false;
         }
-
-        @Override
-        public List<RetraceString> handleMatch(
-            String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
-          final int startOfGroup = matcher.start(captureGroup);
-          if (startOfGroup == NO_MATCH) {
-            if (classNameGroupHandler != null) {
-              for (RetraceString string : strings) {
-                classNameGroupHandler.commitClassName(
-                    original, string, string.context.qualifiedClassContext, matcher);
-              }
-            }
-            return strings;
-          }
-          String methodName = matcher.group(captureGroup);
-          List<RetraceString> retracedStrings = new ArrayList<>();
-          for (RetraceString retraceString : strings) {
-            retraceMethodForString(
-                retraceString,
-                methodName,
-                (element, newContext) -> {
-                  element.visitFrames(
-                      (method, ignoredPosition) -> {
-                        RetraceString newRetraceString =
-                            retraceString.duplicate(newContext.withQualifiedMethodContext(method));
-                        if (classNameGroupHandler != null) {
-                          classNameGroupHandler.commitClassName(
-                              original, newRetraceString, method.getHolderClass(), matcher);
-                        }
-                        retracedStrings.add(
-                            newRetraceString.appendRetracedString(
-                                original,
-                                printVerbose
-                                    ? methodDescriptionFromRetraceMethod(method, false, true)
-                                    : method.getMethodName(),
-                                startOfGroup,
-                                matcher.end(captureGroup)));
-                      });
-                });
-          }
-          return retracedStrings;
-        }
-
-        @Override
-        public RetraceStringContext buildInitial(
-            RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
-          final int startOfGroup = matcher.start(captureGroup);
-          if (startOfGroup == NO_MATCH || context.methodName != null) {
-            return context;
-          }
-          return context.withMethodName(matcher.group(captureGroup));
-        }
+        builder.registerMethodName(startOfGroup, matcher.end(captureGroup));
+        return true;
       };
     }
-
-    private static void retraceMethodForString(
-        RetraceString retraceString,
-        String methodName,
-        BiConsumer<Element, RetraceStringContext> process) {
-      if (retraceString.context.classContext == null) {
-        return;
-      }
-      RetraceClassResult.Element classContext = retraceString.getClassContext();
-      RetraceFrameResult retraceFrameResult =
-          retraceString.context.minifiedLineNumber > NO_MATCH
-              ? classContext.lookupFrame(methodName, retraceString.context.minifiedLineNumber)
-              : classContext.lookupFrame(methodName);
-      retraceFrameResult.forEach(
-          element -> {
-            process.accept(
-                element,
-                retraceString.context.withMethodContext(element, retraceFrameResult.isAmbiguous()));
-          });
-    }
   }
 
   private static class FieldNameGroup extends RegularExpressionGroup {
 
-    private final boolean printVerbose;
-
-    public FieldNameGroup(boolean printVerbose) {
-      this.printVerbose = printVerbose;
-    }
-
     @Override
     String subExpression() {
       return javaIdentifierSegment;
@@ -767,70 +260,15 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return new QualifiedRegularExpressionGroupHandler() {
-
-        private ClassNameGroupHandler classNameGroupHandler;
-
-        @Override
-        public void setClassNameGroupHandler(ClassNameGroupHandler handler) {
-          classNameGroupHandler = handler;
+      return (builder, matcher) -> {
+        int startOfGroup = matcher.start(captureGroup);
+        if (startOfGroup == NO_MATCH) {
+          return false;
         }
-
-        @Override
-        public List<RetraceString> handleMatch(
-            String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
-          final int startOfGroup = matcher.start(captureGroup);
-          if (startOfGroup == NO_MATCH) {
-            if (classNameGroupHandler != null) {
-              for (RetraceString string : strings) {
-                classNameGroupHandler.commitClassName(
-                    original, string, string.context.qualifiedClassContext, matcher);
-              }
-            }
-            return strings;
-          }
-          String fieldName = matcher.group(captureGroup);
-          List<RetraceString> retracedStrings = new ArrayList<>();
-          for (RetraceString retraceString : strings) {
-            if (retraceString.getClassContext() == null) {
-              assert classNameGroupHandler == null;
-              return strings;
-            }
-            final RetraceFieldResult retraceFieldResult =
-                retraceString.getClassContext().lookupField(fieldName);
-            assert !retraceFieldResult.isAmbiguous();
-            retraceFieldResult.forEach(
-                element -> {
-                  if (classNameGroupHandler != null) {
-                    classNameGroupHandler.commitClassName(
-                        original, retraceString, element.getField().getHolderClass(), matcher);
-                  }
-                  retracedStrings.add(
-                      retraceString
-                          .updateContext(
-                              context ->
-                                  context.withQualifiedClassContext(
-                                      element.getField().getHolderClass()))
-                          .appendRetracedString(
-                              original,
-                              getFieldString(element.getField()),
-                              startOfGroup,
-                              matcher.end(captureGroup)));
-                });
-          }
-          return retracedStrings;
-        }
+        builder.registerFieldName(startOfGroup, matcher.end(captureGroup));
+        return true;
       };
     }
-
-    private String getFieldString(RetracedField fieldReference) {
-      if (!printVerbose || fieldReference.isUnknown()) {
-        return fieldReference.getFieldName();
-      }
-      assert fieldReference.isKnown();
-      KnownRetracedField knownRef = fieldReference.asKnown();
-      return knownRef.getFieldType().getTypeName() + " " + fieldReference.getFieldName();
-    }
   }
 
   private static class SourceFileGroup extends RegularExpressionGroup {
@@ -842,40 +280,13 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (original, strings, matcher, retracer) -> {
-        final int startOfGroup = matcher.start(captureGroup);
+      return (builder, matcher) -> {
+        int startOfGroup = matcher.start(captureGroup);
         if (startOfGroup == NO_MATCH) {
-          return strings;
+          return false;
         }
-        String fileName = matcher.group(captureGroup);
-        List<RetraceString> retracedStrings = null;
-        for (RetraceString retraceString : strings) {
-          if (retraceString.context.classContext == null) {
-            return strings;
-          }
-          if (retracedStrings == null) {
-            retracedStrings = new ArrayList<>();
-          }
-          RetraceSourceFileResult sourceFileResult =
-              retraceString.getMethodContext() != null
-                  ? retraceString
-                      .getMethodContext()
-                      .retraceSourceFile(retraceString.context.qualifiedMethodContext, fileName)
-                  : RetraceUtils.getSourceFile(
-                      retraceString.getClassContext(),
-                      retraceString.context.qualifiedClassContext,
-                      fileName,
-                      retracer);
-          retracedStrings.add(
-              retraceString
-                  .updateContext(context -> context.withSource(sourceFileResult.getFilename()))
-                  .appendRetracedString(
-                      original,
-                      sourceFileResult.getFilename(),
-                      startOfGroup,
-                      matcher.end(captureGroup)));
-        }
-        return retracedStrings;
+        builder.registerSourceFile(startOfGroup, matcher.end(captureGroup));
+        return true;
       };
     }
   }
@@ -889,88 +300,13 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return new RegularExpressionGroupHandler() {
-        @Override
-        public List<RetraceString> handleMatch(
-            String original, List<RetraceString> strings, Matcher matcher, RetracerImpl retracer) {
-          final int startOfGroup = matcher.start(captureGroup);
-          if (startOfGroup == NO_MATCH) {
-            return strings;
-          }
-          String lineNumberAsString = matcher.group(captureGroup);
-          if (lineNumberAsString.isEmpty()) {
-            return strings;
-          }
-          int lineNumber = Integer.parseInt(lineNumberAsString);
-          List<RetraceString> retracedStrings = new ArrayList<>();
-          for (RetraceString retraceString : strings) {
-            Element methodContext = retraceString.context.methodContext;
-            if (methodContext == null) {
-              if (retraceString.context.classContext == null
-                  || retraceString.context.methodName == null) {
-                // We have no way of retracing the line number.
-                retracedStrings.add(retraceString);
-                continue;
-              }
-              // This situation arises when we have a matched pattern as %l..%c.%m where the
-              // line number handler is defined before the methodname handler.
-              MethodNameGroup.retraceMethodForString(
-                  retraceString,
-                  retraceString.context.methodName,
-                  (element, newContext) -> {
-                    // The same method can be represented multiple times if it has multiple
-                    // mappings.
-                    element.visitFrames(
-                        (method, ignoredPosition) -> {
-                          int originalPosition = method.getOriginalPositionOrDefault(lineNumber);
-                          retracedStrings.add(
-                              retraceString
-                                  .duplicate(
-                                      retraceString
-                                          .context
-                                          .withQualifiedMethodContext(method)
-                                          .withLineNumbers(lineNumber, originalPosition))
-                                  .appendRetracedString(
-                                      original,
-                                      originalPosition + "",
-                                      startOfGroup,
-                                      matcher.end(captureGroup)));
-                        });
-                  });
-              continue;
-            }
-            // If the method context is unknown, do nothing.
-            if (methodContext.isUnknown()) {
-              retracedStrings.add(retraceString);
-              continue;
-            }
-            int originalLineNumber =
-                retraceString.context.qualifiedMethodContext.getOriginalPositionOrDefault(
-                    lineNumber);
-            retracedStrings.add(
-                retraceString
-                    .updateContext(
-                        context -> context.withLineNumbers(lineNumber, originalLineNumber))
-                    .appendRetracedString(
-                        original,
-                        originalLineNumber + "",
-                        startOfGroup,
-                        matcher.end(captureGroup)));
-          }
-          return retracedStrings;
+      return (builder, matcher) -> {
+        int startOfGroup = matcher.start(captureGroup);
+        if (startOfGroup == NO_MATCH) {
+          return false;
         }
-
-        @Override
-        public RetraceStringContext buildInitial(
-            RetraceStringContext context, Matcher matcher, RetracerImpl retracer) {
-          if (matcher.start(captureGroup) == NO_MATCH || context.minifiedLineNumber > NO_MATCH) {
-            return context;
-          }
-          String lineNumberAsString = matcher.group(captureGroup);
-          return context.withLineNumbers(
-              lineNumberAsString.isEmpty() ? NO_MATCH : Integer.parseInt(lineNumberAsString),
-              NO_MATCH);
-        }
+        builder.registerLineNumber(startOfGroup, matcher.end(captureGroup));
+        return true;
       };
     }
   }
@@ -1005,31 +341,13 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (original, strings, matcher, retracer) -> {
-        final int startOfGroup = matcher.start(captureGroup);
+      return (builder, matcher) -> {
+        int startOfGroup = matcher.start(captureGroup);
         if (startOfGroup == NO_MATCH) {
-          return strings;
+          return false;
         }
-        String typeName = matcher.group(captureGroup);
-        String descriptor = DescriptorUtils.javaTypeToDescriptor(typeName);
-        if (!DescriptorUtils.isDescriptor(descriptor) && !"V".equals(descriptor)) {
-          return strings;
-        }
-        TypeReference typeReference = Reference.returnTypeFromDescriptor(descriptor);
-        RetraceTypeResultImpl retracedType = retracer.retraceType(typeReference);
-        assert !retracedType.isAmbiguous();
-        for (RetraceString retraceString : strings) {
-          retracedType.forEach(
-              element -> {
-                RetracedTypeImpl retracedReference = element.getType();
-                retraceString.appendRetracedString(
-                    original,
-                    retracedReference.isVoid() ? "void" : retracedReference.getTypeName(),
-                    startOfGroup,
-                    matcher.end(captureGroup));
-              });
-        }
-        return strings;
+        builder.registerFieldOrReturnType(startOfGroup, matcher.end(captureGroup));
+        return true;
       };
     }
   }
@@ -1043,37 +361,15 @@
 
     @Override
     RegularExpressionGroupHandler createHandler(String captureGroup) {
-      return (original, strings, matcher, retracer) -> {
-        final int startOfGroup = matcher.start(captureGroup);
+      return (builder, matcher) -> {
+        int startOfGroup = matcher.start(captureGroup);
         if (startOfGroup == NO_MATCH) {
-          return strings;
+          return false;
         }
-        final String formals =
-            Arrays.stream(matcher.group(captureGroup).split(","))
-                .map(
-                    typeName -> {
-                      typeName = typeName.trim();
-                      if (typeName.isEmpty()) {
-                        return null;
-                      }
-                      String descriptor = DescriptorUtils.javaTypeToDescriptor(typeName);
-                      if (!DescriptorUtils.isDescriptor(descriptor) && !"V".equals(descriptor)) {
-                        return typeName;
-                      }
-                      final RetraceTypeResultImpl retraceResult =
-                          retracer.retraceType(Reference.returnTypeFromDescriptor(descriptor));
-                      assert !retraceResult.isAmbiguous();
-                      final Box<RetracedTypeImpl> elementBox = new Box<>();
-                      retraceResult.forEach(element -> elementBox.set(element.getType()));
-                      return elementBox.get().getTypeName();
-                    })
-                .filter(Objects::nonNull)
-                .collect(Collectors.joining(","));
-        for (RetraceString string : strings) {
-          string.appendRetracedString(original, formals, startOfGroup, matcher.end(captureGroup));
-        }
-        return strings;
+        builder.registerMethodArguments(startOfGroup, matcher.end(captureGroup));
+        return true;
       };
     }
   }
 }
+
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
index 8162e05..d467cc1 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
@@ -23,6 +23,10 @@
     return new RetracedTypeImpl(typeReference);
   }
 
+  static RetracedType createVoid() {
+    return new RetracedTypeImpl(null);
+  }
+
   @Override
   public boolean isVoid() {
     return typeReference == null;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index 458978d..a949fc1 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -5,13 +5,23 @@
 package com.android.tools.r8.retrace.internal;
 
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceClassResult;
+import com.android.tools.r8.retrace.RetraceFieldResult;
 import com.android.tools.r8.retrace.RetraceStackTraceProxy;
 import com.android.tools.r8.retrace.RetracedClass;
+import com.android.tools.r8.retrace.RetracedField;
 import com.android.tools.r8.retrace.RetracedMethod;
+import com.android.tools.r8.retrace.RetracedType;
 import com.android.tools.r8.retrace.StackTraceElementProxy;
 import com.android.tools.r8.retrace.StackTraceElementProxyRetracer;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ListUtils;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 public class StackTraceElementProxyRetracerImpl<T extends StackTraceElementProxy<?>>
@@ -28,49 +38,188 @@
     if (!element.hasClassName()) {
       return Stream.of(RetraceStackTraceProxyImpl.builder(element).build());
     }
-    RetraceClassResultImpl classResult =
-        retracer.retraceClass(Reference.classFromTypeName(element.className()));
-    List<RetraceStackTraceProxyImpl<T>> retracedProxies = new ArrayList<>();
-    if (!element.hasMethodName()) {
-      classResult.forEach(
-          classElement -> {
-            RetraceStackTraceProxyImpl.Builder<T> proxy =
-                RetraceStackTraceProxyImpl.builder(element)
-                    .setRetracedClassElement(classElement.getRetracedClass())
-                    .setAmbiguous(classResult.isAmbiguous())
-                    .setTopFrame(true);
-            if (element.hasFileName()) {
-              proxy.setSourceFile(classElement.retraceSourceFile(element.fileName()).getFilename());
-            }
-            retracedProxies.add(proxy.build());
-          });
+    RetraceClassResultImpl classResult = retracer.retraceClass(element.getClassReference());
+    if (element.hasMethodName()) {
+      return retraceMethod(element, classResult);
+    } else if (element.hasFieldName()) {
+      return retraceField(element, classResult);
     } else {
-      RetraceFrameResultImpl frameResult =
-          element.hasLineNumber()
-              ? classResult.lookupFrame(element.methodName(), element.lineNumber())
-              : classResult.lookupFrame(element.methodName());
-      frameResult.forEach(
-          frameElement -> {
-            frameElement.visitFrames(
-                (frame, index) -> {
-                  RetraceStackTraceProxyImpl.Builder<T> proxy =
-                      RetraceStackTraceProxyImpl.builder(element)
-                          .setRetracedClassElement(frame.getHolderClass())
-                          .setRetracedMethodElement(frame)
-                          .setAmbiguous(frameResult.isAmbiguous() && index == 0)
-                          .setTopFrame(index == 0);
-                  if (element.hasLineNumber()) {
-                    proxy.setLineNumber(frame.getOriginalPositionOrDefault(element.lineNumber()));
-                  }
-                  if (element.hasFileName()) {
-                    proxy.setSourceFile(
-                        frameElement.retraceSourceFile(frame, element.fileName()).getFilename());
-                  }
-                  retracedProxies.add(proxy.build());
-                });
-          });
+      return retraceClassOrType(element, classResult);
     }
-    return retracedProxies.stream();
+  }
+
+  private Stream<RetraceStackTraceProxyImpl<T>> retraceClassOrType(
+      T element, RetraceClassResult classResult) {
+    return classResult.stream()
+        .flatMap(
+            classElement ->
+                retraceFieldOrReturnType(element)
+                    .flatMap(
+                        fieldOrReturnTypeConsumer ->
+                            retracedMethodArguments(element)
+                                .map(
+                                    argumentsConsumer -> {
+                                      RetraceStackTraceProxyImpl.Builder<T> proxy =
+                                          RetraceStackTraceProxyImpl.builder(element)
+                                              .setRetracedClass(classElement.getRetracedClass())
+                                              .setAmbiguous(classResult.isAmbiguous())
+                                              .setTopFrame(true);
+                                      argumentsConsumer.accept(proxy);
+                                      fieldOrReturnTypeConsumer.accept(proxy);
+                                      if (element.hasFileName()) {
+                                        proxy.setSourceFile(
+                                            classElement
+                                                .retraceSourceFile(element.getFileName())
+                                                .getFilename());
+                                      }
+                                      return proxy.build();
+                                    })));
+  }
+
+  private Stream<RetraceStackTraceProxyImpl<T>> retraceMethod(
+      T element, RetraceClassResultImpl classResult) {
+    return retraceFieldOrReturnType(element)
+        .flatMap(
+            fieldOrReturnTypeConsumer ->
+                retracedMethodArguments(element)
+                    .flatMap(
+                        argumentsConsumer -> {
+                          RetraceFrameResultImpl frameResult =
+                              element.hasLineNumber()
+                                  ? classResult.lookupFrame(
+                                      element.getMethodName(), element.getLineNumber())
+                                  : classResult.lookupFrame(element.getMethodName());
+                          return frameResult.stream()
+                              .flatMap(
+                                  frameElement -> {
+                                    List<RetraceStackTraceProxyImpl<T>> retracedProxies =
+                                        new ArrayList<>();
+                                    frameElement.visitFrames(
+                                        (frame, index) -> {
+                                          RetraceStackTraceProxyImpl.Builder<T> proxy =
+                                              RetraceStackTraceProxyImpl.builder(element)
+                                                  .setRetracedClass(frame.getHolderClass())
+                                                  .setRetracedMethod(frame)
+                                                  .setAmbiguous(
+                                                      frameResult.isAmbiguous() && index == 0)
+                                                  .setTopFrame(index == 0);
+                                          if (element.hasLineNumber()) {
+                                            proxy.setLineNumber(
+                                                frame.getOriginalPositionOrDefault(
+                                                    element.getLineNumber()));
+                                          }
+                                          if (element.hasFileName()) {
+                                            proxy.setSourceFile(
+                                                frameElement
+                                                    .retraceSourceFile(frame, element.getFileName())
+                                                    .getFilename());
+                                          }
+                                          fieldOrReturnTypeConsumer.accept(proxy);
+                                          argumentsConsumer.accept(proxy);
+                                          retracedProxies.add(proxy.build());
+                                        });
+                                    return retracedProxies.stream();
+                                  });
+                        }));
+  }
+
+  private Stream<RetraceStackTraceProxyImpl<T>> retraceField(
+      T element, RetraceClassResult classResult) {
+    return retraceFieldOrReturnType(element)
+        .flatMap(
+            fieldOrReturnTypeConsumer ->
+                retracedMethodArguments(element)
+                    .flatMap(
+                        argumentsConsumer -> {
+                          RetraceFieldResult retraceFieldResult =
+                              classResult.lookupField(element.getFieldName());
+                          return retraceFieldResult.stream()
+                              .map(
+                                  fieldElement -> {
+                                    RetraceStackTraceProxyImpl.Builder<T> proxy =
+                                        RetraceStackTraceProxyImpl.builder(element)
+                                            .setRetracedClass(
+                                                fieldElement.getField().getHolderClass())
+                                            .setRetracedField(fieldElement.getField())
+                                            .setAmbiguous(retraceFieldResult.isAmbiguous())
+                                            .setTopFrame(true);
+                                    if (element.hasFileName()) {
+                                      proxy.setSourceFile(
+                                          fieldElement
+                                              .retraceSourceFile(element.getFileName())
+                                              .getFilename());
+                                    }
+                                    fieldOrReturnTypeConsumer.accept(proxy);
+                                    argumentsConsumer.accept(proxy);
+                                    return proxy.build();
+                                  });
+                        }));
+  }
+
+  private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T>>> retraceFieldOrReturnType(
+      T element) {
+    if (!element.hasFieldOrReturnType()) {
+      return Stream.of(noEffect -> {});
+    }
+    String elementOrReturnType = element.getFieldOrReturnType();
+    if (elementOrReturnType.equals("void")) {
+      return Stream.of(proxy -> proxy.setRetracedFieldOrReturnType(RetracedTypeImpl.createVoid()));
+    } else {
+      TypeReference typeReference = Reference.typeFromTypeName(elementOrReturnType);
+      RetraceTypeResultImpl retraceTypeResult = retracer.retraceType(typeReference);
+      return retraceTypeResult.stream()
+          .map(
+              type ->
+                  (proxy -> {
+                    proxy.setRetracedFieldOrReturnType(type.getType());
+                    if (retraceTypeResult.isAmbiguous()) {
+                      proxy.setAmbiguous(true);
+                    }
+                  }));
+    }
+  }
+
+  private Stream<Consumer<RetraceStackTraceProxyImpl.Builder<T>>> retracedMethodArguments(
+      T element) {
+    if (!element.hasMethodArguments()) {
+      return Stream.of(noEffect -> {});
+    }
+    List<RetraceTypeResultImpl> retracedResults =
+        Arrays.stream(element.getMethodArguments().split(","))
+            .map(typeName -> retracer.retraceType(Reference.typeFromTypeName(typeName)))
+            .collect(Collectors.toList());
+    List<List<RetracedType>> initial = new ArrayList<>();
+    initial.add(new ArrayList<>());
+    Box<Boolean> isAmbiguous = new Box<>(false);
+    List<List<RetracedType>> retracedArguments =
+        ListUtils.fold(
+            retracedResults,
+            initial,
+            (acc, retracedTypeResult) -> {
+              if (retracedTypeResult.isAmbiguous()) {
+                isAmbiguous.set(true);
+              }
+              List<List<RetracedType>> newResult = new ArrayList<>();
+              retracedTypeResult.forEach(
+                  retracedElement -> {
+                    acc.forEach(
+                        oldResult -> {
+                          List<RetracedType> newList = new ArrayList<>(oldResult);
+                          newList.add(retracedElement.getType());
+                          newResult.add(newList);
+                        });
+                  });
+              return newResult;
+            });
+    return retracedArguments.stream()
+        .map(
+            arguments ->
+                proxy -> {
+                  proxy.setRetracedMethodArguments(arguments);
+                  if (isAmbiguous.get()) {
+                    proxy.setAmbiguous(true);
+                  }
+                });
   }
 
   public static class RetraceStackTraceProxyImpl<T extends StackTraceElementProxy<?>>
@@ -79,6 +228,9 @@
     private final T originalItem;
     private final RetracedClass retracedClass;
     private final RetracedMethod retracedMethod;
+    private final RetracedField retracedField;
+    private final RetracedType fieldOrReturnType;
+    private final List<RetracedType> methodArguments;
     private final String sourceFile;
     private final int lineNumber;
     private final boolean isAmbiguous;
@@ -88,6 +240,9 @@
         T originalItem,
         RetracedClass retracedClass,
         RetracedMethod retracedMethod,
+        RetracedField retracedField,
+        RetracedType fieldOrReturnType,
+        List<RetracedType> methodArguments,
         String sourceFile,
         int lineNumber,
         boolean isAmbiguous,
@@ -96,6 +251,9 @@
       this.originalItem = originalItem;
       this.retracedClass = retracedClass;
       this.retracedMethod = retracedMethod;
+      this.retracedField = retracedField;
+      this.fieldOrReturnType = fieldOrReturnType;
+      this.methodArguments = methodArguments;
       this.sourceFile = sourceFile;
       this.lineNumber = lineNumber;
       this.isAmbiguous = isAmbiguous;
@@ -123,6 +281,11 @@
     }
 
     @Override
+    public boolean hasRetracedField() {
+      return retracedField != null;
+    }
+
+    @Override
     public boolean hasSourceFile() {
       return sourceFile != null;
     }
@@ -133,6 +296,16 @@
     }
 
     @Override
+    public boolean hasFieldOrReturnType() {
+      return fieldOrReturnType != null;
+    }
+
+    @Override
+    public boolean hasMethodArguments() {
+      return methodArguments != null;
+    }
+
+    @Override
     public T getOriginalItem() {
       return originalItem;
     }
@@ -148,6 +321,21 @@
     }
 
     @Override
+    public RetracedField getRetracedField() {
+      return retracedField;
+    }
+
+    @Override
+    public RetracedType getRetracedFieldOrReturnType() {
+      return fieldOrReturnType;
+    }
+
+    @Override
+    public List<RetracedType> getMethodArguments() {
+      return methodArguments;
+    }
+
+    @Override
     public String getSourceFile() {
       return sourceFile;
     }
@@ -209,6 +397,9 @@
       private final T originalElement;
       private RetracedClass classContext;
       private RetracedMethod methodContext;
+      private RetracedField retracedField;
+      private RetracedType fieldOrReturnType;
+      private List<RetracedType> methodArguments;
       private String sourceFile;
       private int lineNumber = -1;
       private boolean isAmbiguous;
@@ -218,16 +409,31 @@
         this.originalElement = originalElement;
       }
 
-      private Builder<T> setRetracedClassElement(RetracedClass retracedClass) {
+      private Builder<T> setRetracedClass(RetracedClass retracedClass) {
         this.classContext = retracedClass;
         return this;
       }
 
-      private Builder<T> setRetracedMethodElement(RetracedMethod methodElement) {
+      private Builder<T> setRetracedMethod(RetracedMethod methodElement) {
         this.methodContext = methodElement;
         return this;
       }
 
+      private Builder<T> setRetracedField(RetracedField retracedField) {
+        this.retracedField = retracedField;
+        return this;
+      }
+
+      private Builder<T> setRetracedFieldOrReturnType(RetracedType retracedType) {
+        this.fieldOrReturnType = retracedType;
+        return this;
+      }
+
+      private Builder<T> setRetracedMethodArguments(List<RetracedType> arguments) {
+        this.methodArguments = arguments;
+        return this;
+      }
+
       private Builder<T> setSourceFile(String sourceFile) {
         this.sourceFile = sourceFile;
         return this;
@@ -257,7 +463,10 @@
             originalElement,
             retracedClass,
             methodContext,
-            sourceFile != null ? sourceFile : null,
+            retracedField,
+            fieldOrReturnType,
+            methodArguments,
+            sourceFile,
             lineNumber,
             isAmbiguous,
             isTopFrame);
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index 15d62ad..f649598 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -8,8 +8,15 @@
 import static com.android.tools.r8.retrace.internal.RetraceUtils.methodDescriptionFromRetraceMethod;
 import static com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StringIndex.noIndex;
 
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.RetracedClass;
+import com.android.tools.r8.retrace.RetracedField;
+import com.android.tools.r8.retrace.RetracedType;
 import com.android.tools.r8.retrace.StackTraceElementProxy;
 import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl.RetraceStackTraceProxyImpl;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.android.tools.r8.utils.TriFunction;
 import java.util.ArrayList;
 import java.util.List;
@@ -18,24 +25,33 @@
 
   private final String line;
   private final List<StringIndex> orderedIndices;
-  private final StringIndex className;
+  private final ClassStringIndex className;
   private final StringIndex methodName;
   private final StringIndex sourceFile;
   private final StringIndex lineNumber;
+  private final StringIndex fieldName;
+  private final StringIndex fieldOrReturnType;
+  private final StringIndex methodArguments;
 
   private StackTraceElementStringProxy(
       String line,
       List<StringIndex> orderedIndices,
-      StringIndex className,
+      ClassStringIndex className,
       StringIndex methodName,
       StringIndex sourceFile,
-      StringIndex lineNumber) {
+      StringIndex lineNumber,
+      StringIndex fieldName,
+      StringIndex fieldOrReturnType,
+      StringIndex methodArguments) {
     this.line = line;
     this.orderedIndices = orderedIndices;
     this.className = className;
     this.methodName = methodName;
     this.sourceFile = sourceFile;
     this.lineNumber = lineNumber;
+    this.fieldName = fieldName;
+    this.fieldOrReturnType = fieldOrReturnType;
+    this.methodArguments = methodArguments;
   }
 
   static StackTraceElementStringProxyBuilder builder(String line) {
@@ -63,22 +79,37 @@
   }
 
   @Override
-  public String className() {
-    return hasClassName() ? getEntryInLine(className) : null;
+  public boolean hasFieldName() {
+    return fieldName.hasIndex();
   }
 
   @Override
-  public String methodName() {
+  public boolean hasFieldOrReturnType() {
+    return fieldOrReturnType.hasIndex();
+  }
+
+  @Override
+  public boolean hasMethodArguments() {
+    return methodArguments.hasIndex();
+  }
+
+  @Override
+  public ClassReference getClassReference() {
+    return hasClassName() ? className.getReference(line) : null;
+  }
+
+  @Override
+  public String getMethodName() {
     return hasMethodName() ? getEntryInLine(methodName) : null;
   }
 
   @Override
-  public String fileName() {
+  public String getFileName() {
     return hasFileName() ? getEntryInLine(sourceFile) : null;
   }
 
   @Override
-  public int lineNumber() {
+  public int getLineNumber() {
     if (!hasLineNumber()) {
       return -1;
     }
@@ -89,6 +120,21 @@
     }
   }
 
+  @Override
+  public String getFieldName() {
+    return hasFieldName() ? getEntryInLine(fieldName) : null;
+  }
+
+  @Override
+  public String getFieldOrReturnType() {
+    return hasFieldOrReturnType() ? getEntryInLine(fieldOrReturnType) : null;
+  }
+
+  @Override
+  public String getMethodArguments() {
+    return hasMethodArguments() ? getEntryInLine(methodArguments) : null;
+  }
+
   public String toRetracedItem(
       RetraceStackTraceProxyImpl<StackTraceElementStringProxy> retracedProxy,
       boolean printAmbiguous,
@@ -118,30 +164,43 @@
     return line.substring(index.startIndex, index.endIndex);
   }
 
+  public enum ClassNameType {
+    BINARY,
+    TYPENAME
+  }
+
   public static class StackTraceElementStringProxyBuilder {
 
     private final String line;
     private final List<StringIndex> orderedIndices = new ArrayList<>();
-    private StringIndex className = noIndex();
+    private ClassStringIndex className = noIndex();
     private StringIndex methodName = noIndex();
     private StringIndex sourceFile = noIndex();
     private StringIndex lineNumber = noIndex();
+    private StringIndex fieldName = noIndex();
+    private StringIndex fieldOrReturnType = noIndex();
+    private StringIndex methodArguments = noIndex();
     private int lastSeenStartIndex = -1;
 
     private StackTraceElementStringProxyBuilder(String line) {
       this.line = line;
     }
 
-    public StackTraceElementStringProxyBuilder registerClassName(int startIndex, int endIndex) {
+    public StackTraceElementStringProxyBuilder registerClassName(
+        int startIndex, int endIndex, ClassNameType classNameType) {
       ensureLineIndexIncreases(startIndex);
       className =
-          new StringIndex(
+          new ClassStringIndex(
               startIndex,
               endIndex,
               (retraced, original, verbose) -> {
                 assert retraced.hasRetracedClass();
-                return retraced.getRetracedClass().getTypeName();
-              });
+                RetracedClass retracedClass = retraced.getRetracedClass();
+                return classNameType == ClassNameType.BINARY
+                    ? retracedClass.getBinaryName()
+                    : retracedClass.getTypeName();
+              },
+              classNameType);
       orderedIndices.add(className);
       return this;
     }
@@ -153,7 +212,7 @@
               endIndex,
               (retraced, original, verbose) -> {
                 if (!retraced.hasRetracedMethod()) {
-                  return original.methodName();
+                  return original.getMethodName();
                 }
                 return methodDescriptionFromRetraceMethod(
                     retraced.getRetracedMethod(), false, verbose);
@@ -168,7 +227,7 @@
               startIndex,
               endIndex,
               (retraced, original, verbose) ->
-                  retraced.hasSourceFile() ? retraced.getSourceFile() : original.fileName());
+                  retraced.hasSourceFile() ? retraced.getSourceFile() : original.getFileName());
       orderedIndices.add(sourceFile);
       return this;
     }
@@ -186,9 +245,73 @@
       return this;
     }
 
+    public StackTraceElementStringProxyBuilder registerFieldName(int startIndex, int endIndex) {
+      fieldName =
+          new StringIndex(
+              startIndex,
+              endIndex,
+              (retraced, original, verbose) -> {
+                if (!retraced.hasRetracedField()) {
+                  return original.getFieldName();
+                }
+                RetracedField retracedField = retraced.getRetracedField();
+                if (!verbose || retracedField.isUnknown()) {
+                  return retracedField.getFieldName();
+                }
+                return retracedField.asKnown().getFieldType().getTypeName()
+                    + " "
+                    + retracedField.getFieldName();
+              });
+      orderedIndices.add(fieldName);
+      return this;
+    }
+
+    public StackTraceElementStringProxyBuilder registerFieldOrReturnType(
+        int startIndex, int endIndex) {
+      fieldOrReturnType =
+          new StringIndex(
+              startIndex,
+              endIndex,
+              (retraced, original, verbose) -> {
+                if (!retraced.hasFieldOrReturnType()) {
+                  return original.getFieldOrReturnType();
+                }
+                return retraced.getRetracedFieldOrReturnType().isVoid()
+                    ? "void"
+                    : retraced.getRetracedFieldOrReturnType().getTypeName();
+              });
+      orderedIndices.add(fieldOrReturnType);
+      return this;
+    }
+
+    public StackTraceElementStringProxyBuilder registerMethodArguments(
+        int startIndex, int endIndex) {
+      methodArguments =
+          new StringIndex(
+              startIndex,
+              endIndex,
+              (retraced, original, verbose) -> {
+                if (!retraced.hasMethodArguments()) {
+                  return original.getMethodArguments();
+                }
+                return StringUtils.join(
+                    retraced.getMethodArguments(), ",", BraceType.NONE, RetracedType::getTypeName);
+              });
+      orderedIndices.add(methodArguments);
+      return this;
+    }
+
     public StackTraceElementStringProxy build() {
       return new StackTraceElementStringProxy(
-          line, orderedIndices, className, methodName, sourceFile, lineNumber);
+          line,
+          orderedIndices,
+          className,
+          methodName,
+          sourceFile,
+          lineNumber,
+          fieldName,
+          fieldOrReturnType,
+          methodArguments);
     }
 
     private void ensureLineIndexIncreases(int newStartIndex) {
@@ -199,16 +322,17 @@
     }
   }
 
-  static final class StringIndex {
+  static class StringIndex {
 
-    private static final StringIndex NO_INDEX = new StringIndex(-1, -1, null);
+    private static final ClassStringIndex NO_INDEX =
+        new ClassStringIndex(-1, -1, null, ClassNameType.TYPENAME);
 
-    static StringIndex noIndex() {
+    static ClassStringIndex noIndex() {
       return NO_INDEX;
     }
 
-    private final int startIndex;
-    private final int endIndex;
+    protected final int startIndex;
+    protected final int endIndex;
     private final TriFunction<
             RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
             StackTraceElementStringProxy,
@@ -234,4 +358,30 @@
       return this != NO_INDEX;
     }
   }
+
+  static final class ClassStringIndex extends StringIndex {
+
+    private final ClassNameType classNameType;
+
+    private ClassStringIndex(
+        int startIndex,
+        int endIndex,
+        TriFunction<
+                RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
+                StackTraceElementStringProxy,
+                Boolean,
+                String>
+            retracedString,
+        ClassNameType classNameType) {
+      super(startIndex, endIndex, retracedString);
+      this.classNameType = classNameType;
+    }
+
+    ClassReference getReference(String line) {
+      String className = line.substring(startIndex, endIndex);
+      return classNameType == ClassNameType.BINARY
+          ? Reference.classFromBinaryName(className)
+          : Reference.classFromTypeName(className);
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index f941485..51821c0 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -7,6 +7,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -63,4 +64,12 @@
     }
     return true;
   }
+
+  public static <T, R> R fold(Collection<T> items, R identity, BiFunction<R, T, R> acc) {
+    R result = identity;
+    for (T item : items) {
+      result = acc.apply(result, item);
+    }
+    return result;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
index b8b1647..6c43c35 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceRegularExpressionTests.java
@@ -63,6 +63,7 @@
 
   @Test
   public void matchMultipleTypeNamesOnLine() {
+    // TODO(b/171691292): We are not supporting having multiple class matches for.
     runRetraceTest(
         "%c\\s%c\\s%c",
         new StackTraceForTest() {
@@ -79,7 +80,7 @@
 
           @Override
           public List<String> retracedStackTrace() {
-            return ImmutableList.of("AA.AA.AA BB.BB.BB CC.CC.CC");
+            return ImmutableList.of("AA.AA.AA b.b.b c.c.c");
           }
 
           @Override
@@ -91,6 +92,7 @@
 
   @Test
   public void matchMultipleSlashNamesOnLine() {
+    // TODO(b/171691292): We are not supporting having multiple class matches for.
     runRetraceTest(
         "%C\\s%C\\s%C",
         new StackTraceForTest() {
@@ -106,7 +108,7 @@
 
           @Override
           public List<String> retracedStackTrace() {
-            return ImmutableList.of("AA/AA BB/BB CC/CC");
+            return ImmutableList.of("AA/AA b/b/b c/c/c");
           }
 
           @Override