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