Extend InstructionSubject to have a back reference to MethodSubject

This will allow for asking questions such as getLineNumber() and
retracePosition() for any instruction.

Bug: 145903423
Change-Id: I139953c753b657e691ceba5398c3df4addec29ff
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionIterator.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionIterator.java
index 0a3263a..6273334 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionIterator.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionIterator.java
@@ -11,10 +11,12 @@
 class CfInstructionIterator implements InstructionIterator {
 
   private final CodeInspector codeInspector;
+  private final MethodSubject method;
   private final Iterator<CfInstruction> iterator;
 
   CfInstructionIterator(CodeInspector codeInspector, MethodSubject method) {
     this.codeInspector = codeInspector;
+    this.method = method;
     assert method.isPresent();
     Code code = method.getMethod().getCode();
     assert code != null && code.isCfCode();
@@ -28,6 +30,6 @@
 
   @Override
   public InstructionSubject next() {
-    return codeInspector.createInstructionSubject(iterator.next());
+    return codeInspector.createInstructionSubject(iterator.next(), method);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index d7ea3ae..a9ea1b7 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -40,10 +40,13 @@
 import org.objectweb.asm.Opcodes;
 
 public class CfInstructionSubject implements InstructionSubject {
-  protected final CfInstruction instruction;
 
-  public CfInstructionSubject(CfInstruction instruction) {
+  protected final CfInstruction instruction;
+  private final MethodSubject method;
+
+  public CfInstructionSubject(CfInstruction instruction, MethodSubject method) {
     this.instruction = instruction;
+    this.method = method;
   }
 
   @Override
@@ -330,6 +333,7 @@
     return instruction instanceof CfArrayStore;
   }
 
+
   @Override
   public int size() {
     // TODO(b/122302789): CfInstruction#getSize()
@@ -343,6 +347,11 @@
   }
 
   @Override
+  public MethodSubject getMethodSubject() {
+    return method;
+  }
+
+  @Override
   public boolean equals(Object other) {
     return other instanceof CfInstructionSubject
         && instruction.equals(((CfInstructionSubject) other).instruction);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 535bceb..4aec544 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -37,6 +37,8 @@
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.RetraceBase;
+import com.android.tools.r8.retrace.RetraceBaseImpl;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.BiMapContainer;
 import com.android.tools.r8.utils.DescriptorUtils;
@@ -335,31 +337,31 @@
     return originalTypeName != null ? originalTypeName : minifiedTypeName;
   }
 
-  InstructionSubject createInstructionSubject(Instruction instruction) {
-    DexInstructionSubject dexInst = new DexInstructionSubject(instruction);
+  InstructionSubject createInstructionSubject(Instruction instruction, MethodSubject method) {
+    DexInstructionSubject dexInst = new DexInstructionSubject(instruction, method);
     if (dexInst.isInvoke()) {
-      return new InvokeDexInstructionSubject(this, instruction);
+      return new InvokeDexInstructionSubject(this, instruction, method);
     } else if (dexInst.isFieldAccess()) {
-      return new FieldAccessDexInstructionSubject(this, instruction);
+      return new FieldAccessDexInstructionSubject(this, instruction, method);
     } else if (dexInst.isNewInstance()) {
-      return new NewInstanceDexInstructionSubject(instruction);
+      return new NewInstanceDexInstructionSubject(instruction, method);
     } else if (dexInst.isConstString(JumboStringMode.ALLOW)) {
-      return new ConstStringDexInstructionSubject(instruction);
+      return new ConstStringDexInstructionSubject(instruction, method);
     } else {
       return dexInst;
     }
   }
 
-  InstructionSubject createInstructionSubject(CfInstruction instruction) {
-    CfInstructionSubject cfInst = new CfInstructionSubject(instruction);
+  InstructionSubject createInstructionSubject(CfInstruction instruction, MethodSubject method) {
+    CfInstructionSubject cfInst = new CfInstructionSubject(instruction, method);
     if (cfInst.isInvoke()) {
-      return new InvokeCfInstructionSubject(this, instruction);
+      return new InvokeCfInstructionSubject(this, instruction, method);
     } else if (cfInst.isFieldAccess()) {
-      return new FieldAccessCfInstructionSubject(this, instruction);
+      return new FieldAccessCfInstructionSubject(this, instruction, method);
     } else if (cfInst.isNewInstance()) {
-      return new NewInstanceCfInstructionSubject(instruction);
+      return new NewInstanceCfInstructionSubject(instruction, method);
     } else if (cfInst.isConstString(JumboStringMode.ALLOW)) {
-      return new ConstStringCfInstructionSubject(instruction);
+      return new ConstStringCfInstructionSubject(instruction, method);
     } else {
       return cfInst;
     }
@@ -462,4 +464,8 @@
       // nothing to do
     }
   }
+
+  public RetraceBase retrace() {
+    return RetraceBaseImpl.create(mapping);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringCfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringCfInstructionSubject.java
index da545e4..c351e10 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringCfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringCfInstructionSubject.java
@@ -10,8 +10,8 @@
 
 public class ConstStringCfInstructionSubject extends CfInstructionSubject
     implements ConstStringInstructionSubject {
-  public ConstStringCfInstructionSubject(CfInstruction instruction) {
-    super(instruction);
+  public ConstStringCfInstructionSubject(CfInstruction instruction, MethodSubject method) {
+    super(instruction, method);
     assert isConstString(JumboStringMode.ALLOW);
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringDexInstructionSubject.java
index cb69144..ddff370 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringDexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ConstStringDexInstructionSubject.java
@@ -11,8 +11,8 @@
 
 public class ConstStringDexInstructionSubject extends DexInstructionSubject
     implements ConstStringInstructionSubject {
-  public ConstStringDexInstructionSubject(Instruction instruction) {
-    super(instruction);
+  public ConstStringDexInstructionSubject(Instruction instruction, MethodSubject method) {
+    super(instruction, method);
     assert isConstString(JumboStringMode.ALLOW);
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionIterator.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionIterator.java
index 8ae5609..0d98b29 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionIterator.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionIterator.java
@@ -12,10 +12,12 @@
 
   private final CodeInspector codeInspector;
   private final DexCode code;
+  private final MethodSubject methodSubject;
   private int index;
 
   DexInstructionIterator(CodeInspector codeInspector, MethodSubject method) {
     this.codeInspector = codeInspector;
+    this.methodSubject = method;
     assert method.isPresent();
     Code code = method.getMethod().getCode();
     assert code != null && code.isDexCode();
@@ -33,6 +35,6 @@
     if (index == code.instructions.length) {
       throw new NoSuchElementException();
     }
-    return codeInspector.createInstructionSubject(code.instructions[index++]);
+    return codeInspector.createInstructionSubject(code.instructions[index++], methodSubject);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 53fb02c..c46c123 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -104,10 +104,13 @@
 import com.android.tools.r8.ir.code.WideConstant;
 
 public class DexInstructionSubject implements InstructionSubject {
-  protected final Instruction instruction;
 
-  public DexInstructionSubject(Instruction instruction) {
+  protected final Instruction instruction;
+  protected final MethodSubject method;
+
+  public DexInstructionSubject(Instruction instruction, MethodSubject method) {
     this.instruction = instruction;
+    this.method = method;
   }
 
   @Override
@@ -438,6 +441,11 @@
   }
 
   @Override
+  public MethodSubject getMethodSubject() {
+    return method;
+  }
+
+  @Override
   public boolean equals(Object other) {
     return other instanceof DexInstructionSubject
         && instruction.equals(((DexInstructionSubject) other).instruction);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java
index ae7d420..2e1d073 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java
@@ -11,8 +11,9 @@
     implements FieldAccessInstructionSubject {
   private final CodeInspector codeInspector;
 
-  public FieldAccessCfInstructionSubject(CodeInspector codeInspector, CfInstruction instruction) {
-    super(instruction);
+  public FieldAccessCfInstructionSubject(
+      CodeInspector codeInspector, CfInstruction instruction, MethodSubject method) {
+    super(instruction, method);
     this.codeInspector = codeInspector;
     assert isFieldAccess();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java
index 361213a..fcfdbbe 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java
@@ -11,8 +11,9 @@
 
   private final CodeInspector codeInspector;
 
-  public FieldAccessDexInstructionSubject(CodeInspector codeInspector, Instruction instruction) {
-    super(instruction);
+  public FieldAccessDexInstructionSubject(
+      CodeInspector codeInspector, Instruction instruction, MethodSubject method) {
+    super(instruction, method);
     this.codeInspector = codeInspector;
     assert isFieldAccess();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 0466c4e..071fb4f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -29,15 +29,18 @@
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.signature.GenericSignatureParser;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.LocalVariableTable.LocalVariableTableEntry;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
-import it.unimi.dsi.fastutil.objects.Reference2IntMap;
-import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 public class FoundMethodSubject extends MethodSubject {
 
@@ -260,14 +263,14 @@
 
   private LineNumberTable getCfLineNumberTable(CfCode code) {
     int currentLine = -1;
-    Reference2IntMap<InstructionSubject> lineNumberTable =
-        new Reference2IntOpenHashMap<>(code.getInstructions().size());
+    Object2IntMap<InstructionSubject> lineNumberTable =
+        new Object2IntOpenHashMap<>(code.getInstructions().size());
     for (CfInstruction insn : code.getInstructions()) {
       if (insn instanceof CfPosition) {
         currentLine = ((CfPosition) insn).getPosition().line;
       }
       if (currentLine != -1) {
-        lineNumberTable.put(new CfInstructionSubject(insn), currentLine);
+        lineNumberTable.put(new CfInstructionSubject(insn, this), currentLine);
       }
     }
     return currentLine == -1 ? null : new LineNumberTable(lineNumberTable);
@@ -278,8 +281,7 @@
     if (debugInfo == null) {
       return null;
     }
-    Reference2IntMap<InstructionSubject> lineNumberTable =
-        new Reference2IntOpenHashMap<>(code.instructions.length);
+    Object2IntMap<InstructionSubject> lineNumberTable = new Object2IntOpenHashMap<>();
     DexDebugPositionState state =
         new DexDebugPositionState(debugInfo.startLine, getMethod().method);
     Iterator<DexDebugEvent> iterator = Arrays.asList(debugInfo.events).iterator();
@@ -288,7 +290,7 @@
       while (state.getCurrentPc() < offset && iterator.hasNext()) {
         iterator.next().accept(state);
       }
-      lineNumberTable.put(new DexInstructionSubject(insn), state.getCurrentLine());
+      lineNumberTable.put(new DexInstructionSubject(insn, this), state.getCurrentLine());
     }
     return new LineNumberTable(lineNumberTable);
   }
@@ -316,8 +318,8 @@
               localVariable.getLocal().signature == null
                   ? null
                   : localVariable.getLocal().signature.toString(),
-              new CfInstructionSubject(localVariable.getStart()),
-              new CfInstructionSubject(localVariable.getEnd())));
+              new CfInstructionSubject(localVariable.getStart(), this),
+              new CfInstructionSubject(localVariable.getEnd(), this)));
     }
     return new LocalVariableTable(builder.build());
   }
@@ -338,4 +340,20 @@
         ? new AbsentAnnotationSubject()
         : new FoundAnnotationSubject(annotation);
   }
+
+  @Override
+  public FoundMethodSubject asFoundMethodSubject() {
+    return this;
+  }
+
+  public MethodReference asMethodReference() {
+    DexMethod method = dexMethod.method;
+    return Reference.method(
+        Reference.classFromDescriptor(method.holder.toDescriptorString()),
+        method.name.toString(),
+        Arrays.stream(method.proto.parameters.values)
+            .map(type -> Reference.typeFromDescriptor(type.toDescriptorString()))
+            .collect(Collectors.toList()),
+        Reference.returnTypeFromDescriptor(method.proto.returnType.toDescriptorString()));
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 25e10f4..9c67ece 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -6,6 +6,8 @@
 
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.retrace.RetraceBase;
+import com.android.tools.r8.retrace.RetraceMethodResult;
 
 public interface InstructionSubject {
 
@@ -107,4 +109,19 @@
   int size();
 
   InstructionOffsetSubject getOffset(MethodSubject methodSubject);
+
+  MethodSubject getMethodSubject();
+
+  default int getLineNumber() {
+    return getMethodSubject().getLineNumberTable().getLineForInstruction(this);
+  }
+
+  default RetraceMethodResult retracePosition(RetraceBase retraceBase) {
+    MethodSubject methodSubject = getMethodSubject();
+    assert methodSubject.isPresent();
+    int lineNumber = getLineNumber();
+    return retraceBase
+        .retrace(methodSubject.asFoundMethodSubject().asMethodReference())
+        .narrowByLine(lineNumber);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java
index c5ceb74..e43ece1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java
@@ -13,8 +13,9 @@
     implements InvokeInstructionSubject {
   private final CodeInspector codeInspector;
 
-  public InvokeCfInstructionSubject(CodeInspector codeInspector, CfInstruction instruction) {
-    super(instruction);
+  public InvokeCfInstructionSubject(
+      CodeInspector codeInspector, CfInstruction instruction, MethodSubject method) {
+    super(instruction, method);
     assert isInvoke();
     this.codeInspector = codeInspector;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java
index 57a0e71..7f5c6ff 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java
@@ -12,8 +12,9 @@
 
   private final CodeInspector codeInspector;
 
-  public InvokeDexInstructionSubject(CodeInspector codeInspector, Instruction instruction) {
-    super(instruction);
+  public InvokeDexInstructionSubject(
+      CodeInspector codeInspector, Instruction instruction, MethodSubject method) {
+    super(instruction, method);
     this.codeInspector = codeInspector;
     assert isInvoke();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/LineNumberTable.java b/src/test/java/com/android/tools/r8/utils/codeinspector/LineNumberTable.java
index 7c9c30f..a90707f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/LineNumberTable.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/LineNumberTable.java
@@ -4,16 +4,20 @@
 package com.android.tools.r8.utils.codeinspector;
 
 import it.unimi.dsi.fastutil.ints.IntCollection;
-import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
 
 public class LineNumberTable {
-  private final Reference2IntMap<InstructionSubject> lineNumberTable;
+  private final Object2IntMap<InstructionSubject> lineNumberTable;
 
-  public LineNumberTable(Reference2IntMap<InstructionSubject> lineNumberTable) {
+  public LineNumberTable(Object2IntMap<InstructionSubject> lineNumberTable) {
     this.lineNumberTable = lineNumberTable;
   }
 
   public IntCollection getLines() {
     return lineNumberTable.values();
   }
+
+  public int getLineForInstruction(InstructionSubject subject) {
+    return lineNumberTable.getInt(subject);
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 4fe5fb0..af6e1a4 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -41,6 +41,10 @@
 
   public abstract boolean isVirtual();
 
+  public FoundMethodSubject asFoundMethodSubject() {
+    return null;
+  }
+
   @Override
   public abstract MethodSignature getOriginalSignature();
 
@@ -82,6 +86,11 @@
     return Streams.stream(iterateInstructions());
   }
 
+  public void getLineNumberForInstruction(InstructionSubject subject) {
+    assert hasLineNumberTable();
+    getLineNumberTable().getLineForInstruction(subject);
+  }
+
   @Override
   public MethodSubject asMethodSubject() {
     return this;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceCfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceCfInstructionSubject.java
index d3b0a93..f4e0a95 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceCfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceCfInstructionSubject.java
@@ -10,8 +10,8 @@
 
 public class NewInstanceCfInstructionSubject extends CfInstructionSubject
     implements NewInstanceInstructionSubject {
-  public NewInstanceCfInstructionSubject(CfInstruction instruction) {
-    super(instruction);
+  public NewInstanceCfInstructionSubject(CfInstruction instruction, MethodSubject method) {
+    super(instruction, method);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceDexInstructionSubject.java
index a396468..ef883fa 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceDexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/NewInstanceDexInstructionSubject.java
@@ -10,8 +10,8 @@
 
 public class NewInstanceDexInstructionSubject extends DexInstructionSubject
     implements NewInstanceInstructionSubject {
-  public NewInstanceDexInstructionSubject(Instruction instruction) {
-    super(instruction);
+  public NewInstanceDexInstructionSubject(Instruction instruction, MethodSubject method) {
+    super(instruction, method);
   }
 
   @Override