MethodLocation.java revision e80efa670f1027fdf3882a298216a460199e38d0
1package org.jf.dexlib2.builder;
2
3import com.google.common.collect.Lists;
4import org.jf.dexlib2.builder.debug.*;
5import org.jf.dexlib2.iface.instruction.Instruction;
6import org.jf.dexlib2.iface.reference.StringReference;
7import org.jf.dexlib2.iface.reference.TypeReference;
8import org.jf.dexlib2.writer.builder.BuilderStringReference;
9
10import javax.annotation.Nonnull;
11import javax.annotation.Nullable;
12import java.util.AbstractSet;
13import java.util.Iterator;
14import java.util.List;
15import java.util.Set;
16
17public class MethodLocation {
18    @Nullable BuilderInstruction instruction;
19    int codeAddress;
20    int index;
21
22    private List<Label> labels = Lists.newArrayList();
23    List<BuilderDebugItem> debugItems = Lists.newArrayList();
24
25    MethodLocation(@Nullable BuilderInstruction instruction,
26    int codeAddress, int index) {
27        this.instruction = instruction;
28        this.codeAddress = codeAddress;
29        this.index = index;
30    }
31
32    @Nullable
33    public Instruction getInstruction() {
34        return instruction;
35    }
36
37    public int getCodeAddress() {
38        return codeAddress;
39    }
40
41    public int getIndex() {
42        return index;
43    }
44
45    void mergeInto(@Nonnull MethodLocation other) {
46        for (Label label: labels) {
47            label.location = other;
48            other.labels.add(label);
49        }
50
51        // We need to keep the debug items in the same order. We add the other debug items to this list, then reassign
52        // the list.
53        for (BuilderDebugItem debugItem: debugItems) {
54            debugItem.location = other;
55        }
56        debugItems.addAll(other.debugItems);
57        other.debugItems = debugItems;
58
59        for (int i=debugItems.size()-1; i>=0; i--) {
60            BuilderDebugItem debugItem = debugItems.get(i);
61            debugItem.location = other;
62            other.debugItems.add(0, debugItem);
63        }
64        for (BuilderDebugItem debugItem: debugItems) {
65            debugItem.location = other;
66            other.debugItems.add(0, debugItem);
67        }
68    }
69
70    @Nonnull
71    public Set<Label> getLabels() {
72        return new AbstractSet<Label>() {
73            @Nonnull
74            @Override public Iterator<Label> iterator() {
75                final Iterator<Label> it = labels.iterator();
76
77                return new Iterator<Label>() {
78                    private @Nullable Label currentLabel = null;
79
80                    @Override public boolean hasNext() {
81                        return it.hasNext();
82                    }
83
84                    @Override public Label next() {
85                        currentLabel = it.next();
86                        return currentLabel;
87                    }
88
89                    @Override public void remove() {
90                        if (currentLabel != null) {
91                            currentLabel.location = null;
92                        }
93                        it.remove();
94                    }
95                };
96            }
97
98            @Override public int size() {
99                return labels.size();
100            }
101
102            @Override public boolean add(@Nonnull Label label) {
103                if (label.isPlaced()) {
104                    throw new IllegalArgumentException("Cannot add a label that is already placed. You must remove " +
105                            "it from its current location first.");
106                }
107                label.location = MethodLocation.this;
108                labels.add(label);
109                return true;
110            }
111        };
112    }
113
114    @Nonnull
115    public Label addNewLabel() {
116        Label label = new Label(this);
117        labels.add(label);
118        return label;
119    }
120
121    @Nonnull
122    public Set<BuilderDebugItem> getDebugItems() {
123        return new AbstractSet<BuilderDebugItem>() {
124            @Nonnull
125            @Override public Iterator<BuilderDebugItem> iterator() {
126                final Iterator<BuilderDebugItem> it = debugItems.iterator();
127
128                return new Iterator<BuilderDebugItem>() {
129                    private @Nullable BuilderDebugItem currentDebugItem = null;
130
131                    @Override public boolean hasNext() {
132                        return it.hasNext();
133                    }
134
135                    @Override public BuilderDebugItem next() {
136                        currentDebugItem = it.next();
137                        return currentDebugItem;
138                    }
139
140                    @Override public void remove() {
141                        if (currentDebugItem != null) {
142                            currentDebugItem.location = null;
143                        }
144                        it.remove();
145                    }
146                };
147            }
148
149            @Override public int size() {
150                return labels.size();
151            }
152
153            @Override public boolean add(@Nonnull BuilderDebugItem debugItem) {
154                if (debugItem.location != null) {
155                    throw new IllegalArgumentException("Cannot add a debug item that has already been added to a " +
156                            "method. You must remove it from its current location first.");
157                }
158                debugItem.location = MethodLocation.this;
159                debugItems.add(debugItem);
160                return true;
161            }
162        };
163    }
164
165    public void addLineNumber(int lineNumber) {
166        debugItems.add(new BuilderLineNumber(this, lineNumber));
167    }
168
169    public void addStartLocal(int registerNumber, @Nullable StringReference name, @Nullable TypeReference type,
170                              @Nullable StringReference signature) {
171        debugItems.add(new BuilderStartLocal(this, registerNumber, name, type, signature));
172    }
173
174    public void addEndLocal(int registerNumber) {
175        debugItems.add(new BuilderEndLocal(this, registerNumber));
176    }
177
178    public void addRestartLocal(int registerNumber) {
179        debugItems.add(new BuilderRestartLocal(this, registerNumber));
180    }
181
182    public void addPrologue() {
183        debugItems.add(new BuilderPrologueEnd(this));
184    }
185
186    public void addEpilogue() {
187        debugItems.add(new BuilderEpilogueBegin(this));
188    }
189
190    public void addSetSourceFile(@Nullable BuilderStringReference sourceFile) {
191        debugItems.add(new BuilderSetSourceFile(this, sourceFile));
192    }
193}
194