1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.generic;
20
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.Map;
25 import java.util.Set;
26
27 import org.apache.bcel.classfile.Utility;
28
29 /**
30 * Instances of this class give users a handle to the instructions contained in an InstructionList. Instruction objects
31 * may be used more than once within a list, this is useful because it saves memory and may be much faster.
32 *
33 * Within an InstructionList an InstructionHandle object is wrapped around all instructions, that is, it implements a cell
34 * in a doubly-linked list. From the outside only the next and the previous instruction (handle) are accessible. One can
35 * traverse the list via an Enumeration returned by InstructionList.elements().
36 *
37 * @see Instruction
38 * @see BranchHandle
39 * @see InstructionList
40 */
41 public class InstructionHandle {
42
43 /**
44 * Empty array.
45 *
46 * @since 6.6.0
47 */
48 public static final InstructionHandle[] EMPTY_ARRAY = {};
49
50 /**
51 * Empty array.
52 */
53 static final InstructionTargeter[] EMPTY_INSTRUCTION_TARGETER_ARRAY = {};
54
55 /**
56 * Factory method.
57 */
58 static InstructionHandle getInstructionHandle(final Instruction i) {
59 return new InstructionHandle(i);
60 }
61
62 private InstructionHandle next;
63 private InstructionHandle prev;
64
65 private Instruction instruction;
66
67 /**
68 * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
69 */
70 @Deprecated
71 protected int i_position = -1; // byte code offset of instruction
72 private Set<InstructionTargeter> targeters;
73
74 private Map<Object, Object> attributes;
75
76 /**
77 * Constructs an InstructionHandle.
78 *
79 * @param i the instruction.
80 */
81 protected InstructionHandle(final Instruction i) {
82 setInstruction(i);
83 }
84
85 /**
86 * Convenience method, simply calls accept() on the contained instruction.
87 *
88 * @param v Visitor object.
89 */
90 public void accept(final Visitor v) {
91 instruction.accept(v);
92 }
93
94 /**
95 * Add an attribute to an instruction handle.
96 *
97 * @param key the key object to store/retrieve the attribute.
98 * @param attr the attribute to associate with this handle.
99 */
100 public void addAttribute(final Object key, final Object attr) {
101 if (attributes == null) {
102 attributes = new HashMap<>(3);
103 }
104 attributes.put(key, attr);
105 }
106
107 /**
108 * Does nothing.
109 *
110 * @deprecated Does nothing as of 6.3.1.
111 */
112 @Deprecated
113 protected void addHandle() {
114 // noop
115 }
116
117 /**
118 * Denote this handle is being referenced by t.
119 *
120 * @param t the instruction targeter.
121 */
122 public void addTargeter(final InstructionTargeter t) {
123 if (targeters == null) {
124 targeters = new HashSet<>();
125 }
126 // if (!targeters.contains(t))
127 targeters.add(t);
128 }
129
130 /**
131 * Delete contents, removes user access.
132 */
133 void dispose() {
134 next = prev = null;
135 instruction.dispose();
136 instruction = null;
137 i_position = -1;
138 attributes = null;
139 removeAllTargeters();
140 }
141
142 /**
143 * Gets attribute of an instruction handle.
144 *
145 * @param key the key object to store/retrieve the attribute.
146 * @return the attribute value.
147 */
148 public Object getAttribute(final Object key) {
149 return attributes != null ? attributes.get(key) : null;
150 }
151
152 /**
153 * Gets all attributes associated with this handle.
154 *
155 * @return all attributes associated with this handle.
156 */
157 public Collection<Object> getAttributes() {
158 if (attributes == null) {
159 attributes = new HashMap<>(3);
160 }
161 return attributes.values();
162 }
163
164 /**
165 * Gets the instruction.
166 *
167 * @return the instruction.
168 */
169 public final Instruction getInstruction() {
170 return instruction;
171 }
172
173 /**
174 * Gets the next instruction handle.
175 *
176 * @return the next instruction handle.
177 */
178 public final InstructionHandle getNext() {
179 return next;
180 }
181
182 /**
183 * Gets the position.
184 *
185 * @return the position, the byte code offset of the contained instruction. This is accurate only after
186 * InstructionList.setPositions() has been called.
187 */
188 public int getPosition() {
189 return i_position;
190 }
191
192 /**
193 * Gets the previous instruction handle.
194 *
195 * @return the previous instruction handle.
196 */
197 public final InstructionHandle getPrev() {
198 return prev;
199 }
200
201 /**
202 * Gets the targeters.
203 *
204 * @return null, if there are no targeters.
205 */
206 public InstructionTargeter[] getTargeters() {
207 if (!hasTargeters()) {
208 return EMPTY_INSTRUCTION_TARGETER_ARRAY;
209 }
210 final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
211 targeters.toArray(t);
212 return t;
213 }
214
215 /**
216 * Checks if this handle has targeters.
217 *
218 * @return true if this handle has targeters, false otherwise.
219 */
220 public boolean hasTargeters() {
221 return targeters != null && !targeters.isEmpty();
222 }
223
224 /**
225 * Remove all targeters, if any.
226 */
227 public void removeAllTargeters() {
228 if (targeters != null) {
229 targeters.clear();
230 }
231 }
232
233 /**
234 * Delete an attribute of an instruction handle.
235 *
236 * @param key the key object to retrieve the attribute.
237 */
238 public void removeAttribute(final Object key) {
239 if (attributes != null) {
240 attributes.remove(key);
241 }
242 }
243
244 /**
245 * Denote this handle isn't referenced anymore by t.
246 *
247 * @param t the instruction targeter.
248 */
249 public void removeTargeter(final InstructionTargeter t) {
250 if (targeters != null) {
251 targeters.remove(t);
252 }
253 }
254
255 /**
256 * Replace current instruction contained in this handle. Old instruction is disposed using Instruction.dispose().
257 *
258 * @param i the new instruction.
259 */
260 public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected?
261 if (i == null) {
262 throw new ClassGenException("Assigning null to handle");
263 }
264 if (this.getClass() != BranchHandle.class && i instanceof BranchInstruction) {
265 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
266 }
267 if (instruction != null) {
268 instruction.dispose();
269 }
270 instruction = i;
271 }
272
273 /**
274 * Sets the next instruction handle.
275 *
276 * @param next the next to set.
277 * @return the next instruction handle.
278 * @since 6.0
279 */
280 final InstructionHandle setNext(final InstructionHandle next) {
281 this.next = next;
282 return next;
283 }
284
285 /**
286 * Sets the position, the byte code offset of the contained instruction.
287 *
288 * @param pos the position.
289 */
290 void setPosition(final int pos) {
291 i_position = pos;
292 }
293
294 /**
295 * Sets the previous instruction handle.
296 *
297 * @param prev the prev to set.
298 * @return the previous instruction handle.
299 * @since 6.0
300 */
301 final InstructionHandle setPrev(final InstructionHandle prev) {
302 this.prev = prev;
303 return prev;
304 }
305
306 /**
307 * Temporarily swap the current instruction, without disturbing anything. Meant to be used by a debugger, implementing
308 * breakpoints. Current instruction is returned.
309 * <p>
310 * Warning: if this is used on a BranchHandle then some methods such as getPosition() will still refer to the original
311 * cached instruction, whereas other BH methods may affect the cache and the replacement instruction.
312 *
313 * @param i the replacement instruction.
314 * @return the old instruction.
315 */
316 // See BCEL-273
317 // TODO remove this method in any redesign of BCEL
318 public Instruction swapInstruction(final Instruction i) {
319 final Instruction oldInstruction = instruction;
320 instruction = i;
321 return oldInstruction;
322 }
323
324 /**
325 * Gets a string representation of the contained instruction.
326 *
327 * @return a string representation of the contained instruction.
328 */
329 @Override
330 public String toString() {
331 return toString(true);
332 }
333
334 /**
335 * Gets a verbose string representation of the contained instruction.
336 *
337 * @param verbose whether to be verbose.
338 * @return a (verbose) string representation of the contained instruction.
339 */
340 public String toString(final boolean verbose) {
341 return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
342 }
343
344 /**
345 * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
346 * length instructions 'setPositions()' performs multiple passes over the instruction list to calculate the correct
347 * (byte) positions and offsets by calling this function.
348 *
349 * @param offset additional offset caused by preceding (variable length) instructions.
350 * @param maxOffset the maximum offset that may be caused by these instructions.
351 * @return additional offset caused by possible change of this instruction's length.
352 */
353 protected int updatePosition(final int offset, final int maxOffset) {
354 i_position += offset;
355 return 0;
356 }
357 }