1// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file 2// for details. All rights reserved. Use of this source code is governed by a 3// BSD-style license that can be found in the LICENSE file. 4package com.android.tools.r8.graph; 5 6import com.android.tools.r8.ir.code.MoveType; 7import com.google.common.collect.ImmutableMap; 8import java.util.ArrayList; 9import java.util.HashMap; 10import java.util.List; 11import java.util.Map; 12 13/** 14 * Builder to construct a "per position" representation of the debug information. 15 * 16 * This builder is relatively relaxed about the stream of build operations and should accept 17 * any stream from any input file we expect to process correctly. 18 */ 19public class DexDebugEntryBuilder { 20 21 private static class LocalEntry { 22 DebugLocalInfo current; 23 DebugLocalInfo last; 24 25 void set(DebugLocalInfo value) { 26 current = value; 27 last = value; 28 } 29 30 void unset() { 31 current = null; 32 } 33 34 void reset() { 35 current = last; 36 } 37 } 38 39 // The variables of the state machine. 40 private int currentPc = 0; 41 private int currentLine; 42 private DexString currentFile = null; 43 private boolean prologueEnd = false; 44 private boolean epilogueBegin = false; 45 private final Map<Integer, LocalEntry> locals = new HashMap<>(); 46 47 // Delayed construction of an entry. Is finalized once locals information has been collected. 48 private DexDebugEntry pending = null; 49 50 // Canonicalization of locals (the IR/Dex builders assume identity of locals). 51 private final Map<DebugLocalInfo, DebugLocalInfo> canonicalizedLocals = new HashMap<>(); 52 53 // Resulting debug entries. 54 private List<DexDebugEntry> entries = new ArrayList<>(); 55 56 public DexDebugEntryBuilder(int startLine) { 57 currentLine = startLine; 58 } 59 60 public DexDebugEntryBuilder(DexEncodedMethod method, DexItemFactory factory) { 61 DexCode code = method.getCode().asDexCode(); 62 DexDebugInfo info = code.getDebugInfo(); 63 int argumentRegister = code.registerSize - code.incomingRegisterSize; 64 if (!method.accessFlags.isStatic()) { 65 DexString name = factory.thisName; 66 DexType type = method.method.getHolder(); 67 startLocal(argumentRegister, name, type, null); 68 argumentRegister += MoveType.fromDexType(type).requiredRegisters(); 69 } 70 DexType[] types = method.method.proto.parameters.values; 71 DexString[] names = info.parameters; 72 for (int i = 0; i < types.length; i++) { 73 // If null, the parameter has a parameterized type and the local is introduced in the stream. 74 if (names[i] != null) { 75 startLocal(argumentRegister, names[i], types[i], null); 76 argumentRegister += MoveType.fromDexType(types[i]).requiredRegisters(); 77 } 78 } 79 currentLine = info.startLine; 80 for (DexDebugEvent event : info.events) { 81 event.addToBuilder(this); 82 } 83 } 84 85 public void setFile(DexString file) { 86 currentFile = file; 87 } 88 89 public void advancePC(int pcDelta) { 90 assert pcDelta >= 0; 91 currentPc += pcDelta; 92 } 93 94 public void advanceLine(int line) { 95 currentLine += line; 96 } 97 98 public void endPrologue() { 99 prologueEnd = true; 100 } 101 102 public void beginEpilogue() { 103 epilogueBegin = true; 104 } 105 106 public void startLocal(int register, DexString name, DexType type, DexString signature) { 107 getEntry(register).set(canonicalize(name, type, signature)); 108 } 109 110 public void endLocal(int register) { 111 getEntry(register).unset(); 112 } 113 114 public void restartLocal(int register) { 115 getEntry(register).reset(); 116 } 117 118 public void setPosition(int pcDelta, int lineDelta) { 119 assert pcDelta >= 0; 120 if (pending != null) { 121 // Local changes contribute to the pending position entry. 122 entries.add(new DexDebugEntry( 123 pending.address, pending.line, pending.sourceFile, 124 pending.prologueEnd, pending.epilogueBegin, 125 getLocals())); 126 } 127 currentPc += pcDelta; 128 currentLine += lineDelta; 129 pending = new DexDebugEntry( 130 currentPc, currentLine, currentFile, prologueEnd, epilogueBegin, null); 131 prologueEnd = false; 132 epilogueBegin = false; 133 } 134 135 public List<DexDebugEntry> build() { 136 // Flush any pending entry. 137 if (pending != null) { 138 setPosition(0, 0); 139 pending = null; 140 } 141 List<DexDebugEntry> result = entries; 142 entries = null; 143 return result; 144 } 145 146 private DebugLocalInfo canonicalize(DexString name, DexType type, DexString signature) { 147 DebugLocalInfo local = new DebugLocalInfo(name, type, signature); 148 DebugLocalInfo canonical = canonicalizedLocals.putIfAbsent(local, local); 149 return canonical != null ? canonical : local; 150 } 151 152 private LocalEntry getEntry(int register) { 153 LocalEntry entry = locals.get(register); 154 if (entry == null) { 155 entry = new LocalEntry(); 156 locals.put(register, entry); 157 } 158 return entry; 159 } 160 161 private ImmutableMap<Integer, DebugLocalInfo> getLocals() { 162 ImmutableMap.Builder<Integer, DebugLocalInfo> builder = ImmutableMap.builder(); 163 for (Integer register : locals.keySet()) { 164 LocalEntry entry = locals.get(register); 165 if (entry.current != null) { 166 builder.put(register, entry.current); 167 } 168 } 169 return builder.build(); 170 } 171} 172