Refactor DexDebugEvent/EntryBuilder into visitor pattern, also ...

... extract the position-related part of the state machine in
DexDebugEntryBuilder into a standalone class PositionalDebugEventCollector.

Bug:
Change-Id: I55b404f4c9d14ec720da81d28a0130f80be33592
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index 6ca2268..38195ae 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.ValueType;
 import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
@@ -17,10 +16,10 @@
 /**
  * Builder to construct a "per position" representation of the debug information.
  *
- * This builder is relatively relaxed about the stream of build operations and should accept
- * any stream from any input file we expect to process correctly.
+ * <p>This builder is relatively relaxed about the stream of build operations and should accept any
+ * stream from any input file we expect to process correctly.
  */
-public class DexDebugEntryBuilder {
+public class DexDebugEntryBuilder implements DexDebugEventVisitor {
 
   private static class LocalEntry {
     DebugLocalInfo current;
@@ -41,11 +40,6 @@
   }
 
   // The variables of the state machine.
-  private int currentPc = 0;
-  private int currentLine;
-  private DexString currentFile = null;
-  private DexMethod currentMethod = null;
-  private Position currentCallerPosition = null;
   private boolean prologueEnd = false;
   private boolean epilogueBegin = false;
   private final Map<Integer, LocalEntry> locals = new HashMap<>();
@@ -60,17 +54,20 @@
 
   // Resulting debug entries.
   private List<DexDebugEntry> entries = new ArrayList<>();
+  private DexDebugPositionState positionState;
 
   public DexDebugEntryBuilder(int startLine, DexMethod method) {
-    currentLine = startLine;
+    assert method != null;
     this.method = method;
-    assert this.method != null;
-    currentMethod = method;
+    positionState = new DexDebugPositionState(startLine, method);
   }
 
   public DexDebugEntryBuilder(DexEncodedMethod method, DexItemFactory factory) {
+    assert method != null && method.method != null;
     this.method = method.method;
-    assert this.method != null;
+    positionState =
+        new DexDebugPositionState(
+            method.getCode().asDexCode().getDebugInfo().startLine, method.method);
     DexCode code = method.getCode().asDexCode();
     DexDebugInfo info = code.getDebugInfo();
     int argumentRegister = code.registerSize - code.incomingRegisterSize;
@@ -89,10 +86,8 @@
       }
       argumentRegister += ValueType.fromDexType(types[i]).requiredRegisters();
     }
-    currentLine = info.startLine;
-    currentMethod = this.method;
     for (DexDebugEvent event : info.events) {
-      event.addToBuilder(this);
+      event.accept(this);
     }
   }
 
@@ -100,31 +95,39 @@
     return arguments;
   }
 
-  public void setFile(DexString file) {
-    currentFile = file;
+  @Override
+  public void visit(DexDebugEvent.AdvancePC advancePC) {
+    positionState.visit(advancePC);
   }
 
-  public void setInlineFrame(DexMethod callee, Position caller) {
-    assert (caller == null && callee == method)
-        || (caller != null && caller.getOutermostCaller().method == method);
-    currentMethod = callee;
-    currentCallerPosition = caller;
+  @Override
+  public void visit(DexDebugEvent.AdvanceLine advanceLine) {
+    positionState.visit(advanceLine);
   }
 
-  public void advancePC(int pcDelta) {
-    assert pcDelta >= 0;
-    currentPc += pcDelta;
+  @Override
+  public void visit(DexDebugEvent.SetInlineFrame setInlineFrame) {
+    positionState.visit(setInlineFrame);
   }
 
-  public void advanceLine(int line) {
-    currentLine += line;
+  @Override
+  public void visit(DexDebugEvent.Default defaultEvent) {
+    positionState.visit(defaultEvent);
+    defaultEventReceived();
   }
 
-  public void endPrologue() {
+  @Override
+  public void visit(DexDebugEvent.SetFile setFile) {
+    positionState.visit(setFile);
+  }
+
+  @Override
+  public void visit(DexDebugEvent.SetPrologueEnd setPrologueEnd) {
     prologueEnd = true;
   }
 
-  public void beginEpilogue() {
+  @Override
+  public void visit(DexDebugEvent.SetEpilogueBegin setEpilogueBegin) {
     epilogueBegin = true;
   }
 
@@ -134,20 +137,23 @@
     getEntry(register).set(argument);
   }
 
-  public void startLocal(int register, DexString name, DexType type, DexString signature) {
-    getEntry(register).set(canonicalize(name, type, signature));
+  @Override
+  public void visit(DexDebugEvent.StartLocal setStartLocal) {
+    getEntry(setStartLocal.registerNum)
+        .set(canonicalize(setStartLocal.name, setStartLocal.type, setStartLocal.signature));
   }
 
-  public void endLocal(int register) {
-    getEntry(register).unset();
+  @Override
+  public void visit(DexDebugEvent.EndLocal endLocal) {
+    getEntry(endLocal.registerNum).unset();
   }
 
-  public void restartLocal(int register) {
-    getEntry(register).reset();
+  @Override
+  public void visit(DexDebugEvent.RestartLocal restartLocal) {
+    getEntry(restartLocal.registerNum).reset();
   }
 
-  public void setPosition(int pcDelta, int lineDelta) {
-    assert pcDelta >= 0;
+  public void defaultEventReceived() {
     if (pending != null) {
       // Local changes contribute to the pending position entry.
       entries.add(
@@ -161,18 +167,16 @@
               pending.method,
               pending.callerPosition));
     }
-    currentPc += pcDelta;
-    currentLine += lineDelta;
     pending =
         new DexDebugEntry(
-            currentPc,
-            currentLine,
-            currentFile,
+            positionState.getCurrentPc(),
+            positionState.getCurrentLine(),
+            positionState.getCurrentFile(),
             prologueEnd,
             epilogueBegin,
             null,
-            currentMethod,
-            currentCallerPosition);
+            positionState.getCurrentMethod(),
+            positionState.getCurrentCallerPosition());
     prologueEnd = false;
     epilogueBegin = false;
   }
@@ -180,7 +184,7 @@
   public List<DexDebugEntry> build() {
     // Flush any pending entry.
     if (pending != null) {
-      setPosition(0, 0);
+      defaultEventReceived(); // To flush 'pending'.
       pending = null;
     }
     List<DexDebugEntry> result = entries;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 724c10b..9d42c82 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -34,7 +34,7 @@
 
   public abstract void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping);
 
-  public abstract void addToBuilder(DexDebugEntryBuilder builder);
+  public abstract void accept(DexDebugEventVisitor visitor);
 
   public static class AdvancePC extends DexDebugEvent {
 
@@ -51,11 +51,12 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
+    public void accept(DexDebugEventVisitor visitor) {
       assert delta >= 0;
-      builder.advancePC(delta);
+      visitor.visit(this);
     }
 
+
     @Override
     public String toString() {
       return "ADVANCE_PC " + delta;
@@ -85,8 +86,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.endPrologue();
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -118,8 +119,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.beginEpilogue();
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -153,8 +154,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.advanceLine(delta);
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -220,8 +221,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.startLocal(registerNum, name, type, signature);
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -272,8 +273,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.endLocal(registerNum);
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -309,8 +310,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.restartLocal(registerNum);
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -339,14 +340,6 @@
       this.fileName = fileName;
     }
 
-    public DexString getFileName() {
-      return fileName;
-    }
-
-    public void setFileName(DexString fileName) {
-      this.fileName = fileName;
-    }
-
     @Override
     public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
       writer.putByte(Constants.DBG_SET_FILE);
@@ -359,8 +352,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.setFile(fileName);
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -398,8 +391,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      builder.setInlineFrame(callee, caller);
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     @Override
@@ -437,11 +430,8 @@
     }
 
     @Override
-    public void addToBuilder(DexDebugEntryBuilder builder) {
-      int adjustedOpcode = value - Constants.DBG_FIRST_SPECIAL;
-      int line = Constants.DBG_LINE_BASE + (adjustedOpcode % Constants.DBG_LINE_RANGE);
-      int address = adjustedOpcode / Constants.DBG_LINE_RANGE;
-      builder.setPosition(address, line);
+    public void accept(DexDebugEventVisitor visitor) {
+      visitor.visit(this);
     }
 
     public int getPCDelta() {
@@ -456,7 +446,7 @@
 
     @Override
     public String toString() {
-      return "DEFAULT " + value;
+      return String.format("DEFAULT %d (dpc %d, %dline %d)", value, getPCDelta(), getLineDelta());
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventVisitor.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventVisitor.java
new file mode 100644
index 0000000..464c93d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventVisitor.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
+import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
+import com.android.tools.r8.graph.DexDebugEvent.EndLocal;
+import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
+import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
+import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
+import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
+import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+
+public interface DexDebugEventVisitor {
+  void visit(AdvancePC advancePC);
+
+  void visit(AdvanceLine advanceLine);
+
+  void visit(SetInlineFrame setInlineFrame);
+
+  void visit(Default defaultEvent);
+
+  void visit(SetFile setFile);
+
+  void visit(SetPrologueEnd setPrologueEnd);
+
+  void visit(SetEpilogueBegin setEpilogueBegin);
+
+  void visit(StartLocal startLocal);
+
+  void visit(EndLocal endLocal);
+
+  void visit(RestartLocal restartLocal);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 99d1227..4b8c4b8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -27,7 +27,7 @@
   public List<DexDebugEntry> computeEntries(DexMethod method) {
     DexDebugEntryBuilder builder = new DexDebugEntryBuilder(startLine, method);
     for (DexDebugEvent event : events) {
-      event.addToBuilder(builder);
+      event.accept(builder);
     }
     return builder.build();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
new file mode 100644
index 0000000..9a551db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
@@ -0,0 +1,105 @@
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
+import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
+import com.android.tools.r8.graph.DexDebugEvent.EndLocal;
+import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
+import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
+import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexDebugEvent.SetInlineFrame;
+import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
+import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.ir.code.Position;
+
+/**
+ * State machine to process and accumulate position-related DexDebugEvents. Clients should retrieve
+ * the current state using the getters after a Default event.
+ */
+public class DexDebugPositionState implements DexDebugEventVisitor {
+  private final DexMethod method;
+
+  private int currentPc = 0;
+  private int currentLine;
+  private DexString currentFile = null;
+  private DexMethod currentMethod = null;
+  private Position currentCallerPosition = null;
+
+  public DexDebugPositionState(int startLine, DexMethod method) {
+    this.method = method;
+    currentLine = startLine;
+    currentMethod = method;
+  }
+
+  public void visit(AdvancePC advancePC) {
+    assert advancePC.delta >= 0;
+    currentPc += advancePC.delta;
+  }
+
+  public void visit(AdvanceLine advanceLine) {
+    currentLine += advanceLine.delta;
+  }
+
+  public void visit(SetInlineFrame setInlineFrame) {
+    assert (setInlineFrame.caller == null && setInlineFrame.callee == method)
+        || (setInlineFrame.caller != null
+            && setInlineFrame.caller.getOutermostCaller().method == method);
+    currentMethod = setInlineFrame.callee;
+    currentCallerPosition = setInlineFrame.caller;
+  }
+
+  public void visit(Default defaultEvent) {
+    assert defaultEvent.getPCDelta() >= 0;
+    currentPc += defaultEvent.getPCDelta();
+    currentLine += defaultEvent.getLineDelta();
+  }
+
+  public void visit(SetFile setFile) {
+    currentFile = setFile.fileName;
+  }
+
+  @Override
+  public void visit(SetPrologueEnd setPrologueEnd) {
+    // Empty.
+  }
+
+  @Override
+  public void visit(SetEpilogueBegin setEpilogueBegin) {
+    // Empty.
+  }
+
+  @Override
+  public void visit(StartLocal startLocal) {
+    // Empty.
+  }
+
+  @Override
+  public void visit(EndLocal endLocal) {
+    // Empty.
+  }
+
+  @Override
+  public void visit(RestartLocal restartLocal) {
+    // Empty.
+  }
+
+  public int getCurrentPc() {
+    return currentPc;
+  }
+
+  public int getCurrentLine() {
+    return currentLine;
+  }
+
+  public DexString getCurrentFile() {
+    return currentFile;
+  }
+
+  public DexMethod getCurrentMethod() {
+    return currentMethod;
+  }
+
+  public Position getCurrentCallerPosition() {
+    return currentCallerPosition;
+  }
+}