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, i.e., 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 protected InstructionHandle(final Instruction i) {
77 setInstruction(i);
78 }
79
80 /**
81 * Convenience method, simply calls accept() on the contained instruction.
82 *
83 * @param v Visitor object
84 */
85 public void accept(final Visitor v) {
86 instruction.accept(v);
87 }
88
89 /**
90 * Add an attribute to an instruction handle.
91 *
92 * @param key the key object to store/retrieve the attribute
93 * @param attr the attribute to associate with this handle
94 */
95 public void addAttribute(final Object key, final Object attr) {
96 if (attributes == null) {
97 attributes = new HashMap<>(3);
98 }
99 attributes.put(key, attr);
100 }
101
102 /**
103 * Does nothing.
104 *
105 * @deprecated Does nothing as of 6.3.1.
106 */
107 @Deprecated
108 protected void addHandle() {
109 // noop
110 }
111
112 /**
113 * Denote this handle is being referenced by t.
114 */
115 public void addTargeter(final InstructionTargeter t) {
116 if (targeters == null) {
117 targeters = new HashSet<>();
118 }
119 // if (!targeters.contains(t))
120 targeters.add(t);
121 }
122
123 /**
124 * Delete contents, i.e., remove user access.
125 */
126 void dispose() {
127 next = prev = null;
128 instruction.dispose();
129 instruction = null;
130 i_position = -1;
131 attributes = null;
132 removeAllTargeters();
133 }
134
135 /**
136 * Gets attribute of an instruction handle.
137 *
138 * @param key the key object to store/retrieve the attribute
139 */
140 public Object getAttribute(final Object key) {
141 return attributes != null ? attributes.get(key) : null;
142 }
143
144 /**
145 * @return all attributes associated with this handle
146 */
147 public Collection<Object> getAttributes() {
148 if (attributes == null) {
149 attributes = new HashMap<>(3);
150 }
151 return attributes.values();
152 }
153
154 public final Instruction getInstruction() {
155 return instruction;
156 }
157
158 public final InstructionHandle getNext() {
159 return next;
160 }
161
162 /**
163 * @return the position, i.e., the byte code offset of the contained instruction. This is accurate only after
164 * InstructionList.setPositions() has been called.
165 */
166 public int getPosition() {
167 return i_position;
168 }
169
170 public final InstructionHandle getPrev() {
171 return prev;
172 }
173
174 /**
175 * @return null, if there are no targeters
176 */
177 public InstructionTargeter[] getTargeters() {
178 if (!hasTargeters()) {
179 return EMPTY_INSTRUCTION_TARGETER_ARRAY;
180 }
181 final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
182 targeters.toArray(t);
183 return t;
184 }
185
186 public boolean hasTargeters() {
187 return targeters != null && !targeters.isEmpty();
188 }
189
190 /**
191 * Remove all targeters, if any.
192 */
193 public void removeAllTargeters() {
194 if (targeters != null) {
195 targeters.clear();
196 }
197 }
198
199 /**
200 * Delete an attribute of an instruction handle.
201 *
202 * @param key the key object to retrieve the attribute
203 */
204 public void removeAttribute(final Object key) {
205 if (attributes != null) {
206 attributes.remove(key);
207 }
208 }
209
210 /**
211 * Denote this handle isn't referenced anymore by t.
212 */
213 public void removeTargeter(final InstructionTargeter t) {
214 if (targeters != null) {
215 targeters.remove(t);
216 }
217 }
218
219 /**
220 * Replace current instruction contained in this handle. Old instruction is disposed using Instruction.dispose().
221 */
222 public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected?
223 if (i == null) {
224 throw new ClassGenException("Assigning null to handle");
225 }
226 if (this.getClass() != BranchHandle.class && i instanceof BranchInstruction) {
227 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
228 }
229 if (instruction != null) {
230 instruction.dispose();
231 }
232 instruction = i;
233 }
234
235 /**
236 * @param next the next to set
237 * @since 6.0
238 */
239 final InstructionHandle setNext(final InstructionHandle next) {
240 this.next = next;
241 return next;
242 }
243
244 /**
245 * Sets the position, i.e., the byte code offset of the contained instruction.
246 */
247 void setPosition(final int pos) {
248 i_position = pos;
249 }
250
251 /**
252 * @param prev the prev to set
253 * @since 6.0
254 */
255 final InstructionHandle setPrev(final InstructionHandle prev) {
256 this.prev = prev;
257 return prev;
258 }
259
260 /**
261 * Temporarily swap the current instruction, without disturbing anything. Meant to be used by a debugger, implementing
262 * breakpoints. Current instruction is returned.
263 * <p>
264 * Warning: if this is used on a BranchHandle then some methods such as getPosition() will still refer to the original
265 * cached instruction, whereas other BH methods may affect the cache and the replacement instruction.
266 */
267 // See BCEL-273
268 // TODO remove this method in any redesign of BCEL
269 public Instruction swapInstruction(final Instruction i) {
270 final Instruction oldInstruction = instruction;
271 instruction = i;
272 return oldInstruction;
273 }
274
275 /**
276 * @return a string representation of the contained instruction.
277 */
278 @Override
279 public String toString() {
280 return toString(true);
281 }
282
283 /**
284 * @return a (verbose) string representation of the contained instruction.
285 */
286 public String toString(final boolean verbose) {
287 return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
288 }
289
290 /**
291 * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
292 * length instructions 'setPositions()' performs multiple passes over the instruction list to calculate the correct
293 * (byte) positions and offsets by calling this function.
294 *
295 * @param offset additional offset caused by preceding (variable length) instructions
296 * @param maxOffset the maximum offset that may be caused by these instructions
297 * @return additional offset caused by possible change of this instruction's length
298 */
299 protected int updatePosition(final int offset, final int maxOffset) {
300 i_position += offset;
301 return 0;
302 }
303 }