1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 package org.apache.bcel.generic;
19
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25 import org.apache.bcel.classfile.Utility;
26
27 /**
28 * Instances of this class give users a handle to the instructions contained in
29 * an InstructionList. Instruction objects may be used more than once within a
30 * list, this is useful because it saves memory and may be much faster.
31 *
32 * Within an InstructionList an InstructionHandle object is wrapped
33 * around all instructions, i.e., it implements a cell in a
34 * doubly-linked list. From the outside only the next and the
35 * previous instruction (handle) are accessible. One
36 * can traverse the list via an Enumeration returned by
37 * InstructionList.elements().
38 *
39 * @version $Id: InstructionHandle.java 1152072 2011-07-29 01:54:05Z dbrosius $
40 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
41 * @see Instruction
42 * @see BranchHandle
43 * @see InstructionList
44 */
45 public class InstructionHandle implements java.io.Serializable {
46
47 private static final long serialVersionUID = -3585254135692924106L;
48 InstructionHandle next, prev; // Will be set from the outside
49 Instruction instruction;
50 protected int i_position = -1; // byte code offset of instruction
51 private Set<InstructionTargeter> targeters;
52 private Map<Object, Object> attributes;
53
54
55 public final InstructionHandle getNext() {
56 return next;
57 }
58
59
60 public final InstructionHandle getPrev() {
61 return prev;
62 }
63
64
65 public final Instruction getInstruction() {
66 return instruction;
67 }
68
69
70 /**
71 * Replace current instruction contained in this handle.
72 * Old instruction is disposed using Instruction.dispose().
73 */
74 public void setInstruction( Instruction i ) { // Overridden in BranchHandle
75 if (i == null) {
76 throw new ClassGenException("Assigning null to handle");
77 }
78 if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
79 throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
80 }
81 if (instruction != null) {
82 instruction.dispose();
83 }
84 instruction = i;
85 }
86
87
88 /**
89 * Temporarily swap the current instruction, without disturbing
90 * anything. Meant to be used by a debugger, implementing
91 * breakpoints. Current instruction is returned.
92 */
93 public Instruction swapInstruction( Instruction i ) {
94 Instruction oldInstruction = instruction;
95 instruction = i;
96 return oldInstruction;
97 }
98
99
100 /*private*/protected InstructionHandle(Instruction i) {
101 setInstruction(i);
102 }
103
104 private static InstructionHandle ih_list = null; // List of reusable handles
105
106
107 /** Factory method.
108 */
109 static final InstructionHandle getInstructionHandle( Instruction i ) {
110 if (ih_list == null) {
111 return new InstructionHandle(i);
112 } else {
113 InstructionHandle ih = ih_list;
114 ih_list = ih.next;
115 ih.setInstruction(i);
116 return ih;
117 }
118 }
119
120
121 /**
122 * Called by InstructionList.setPositions when setting the position for every
123 * instruction. In the presence of variable length instructions `setPositions()'
124 * performs multiple passes over the instruction list to calculate the
125 * correct (byte) positions and offsets by calling this function.
126 *
127 * @param offset additional offset caused by preceding (variable length) instructions
128 * @param max_offset the maximum offset that may be caused by these instructions
129 * @return additional offset caused by possible change of this instruction's length
130 */
131 protected int updatePosition( int offset, int max_offset ) {
132 i_position += offset;
133 return 0;
134 }
135
136
137 /** @return the position, i.e., the byte code offset of the contained
138 * instruction. This is accurate only after
139 * InstructionList.setPositions() has been called.
140 */
141 public int getPosition() {
142 return i_position;
143 }
144
145
146 /** Set the position, i.e., the byte code offset of the contained
147 * instruction.
148 */
149 void setPosition( int pos ) {
150 i_position = pos;
151 }
152
153
154 /** Overridden in BranchHandle
155 */
156 protected void addHandle() {
157 next = ih_list;
158 ih_list = this;
159 }
160
161
162 /**
163 * Delete contents, i.e., remove user access and make handle reusable.
164 */
165 void dispose() {
166 next = prev = null;
167 instruction.dispose();
168 instruction = null;
169 i_position = -1;
170 attributes = null;
171 removeAllTargeters();
172 addHandle();
173 }
174
175
176 /** Remove all targeters, if any.
177 */
178 public void removeAllTargeters() {
179 if (targeters != null) {
180 targeters.clear();
181 }
182 }
183
184
185 /**
186 * Denote this handle isn't referenced anymore by t.
187 */
188 public void removeTargeter( InstructionTargeter t ) {
189 if (targeters != null) {
190 targeters.remove(t);
191 }
192 }
193
194
195 /**
196 * Denote this handle is being referenced by t.
197 */
198 public void addTargeter( InstructionTargeter t ) {
199 if (targeters == null) {
200 targeters = new HashSet<InstructionTargeter>();
201 }
202 //if(!targeters.contains(t))
203 targeters.add(t);
204 }
205
206
207 public boolean hasTargeters() {
208 return (targeters != null) && (targeters.size() > 0);
209 }
210
211
212 /**
213 * @return null, if there are no targeters
214 */
215 public InstructionTargeter[] getTargeters() {
216 if (!hasTargeters()) {
217 return new InstructionTargeter[0];
218 }
219 InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
220 targeters.toArray(t);
221 return t;
222 }
223
224
225 /** @return a (verbose) string representation of the contained instruction.
226 */
227 public String toString( boolean verbose ) {
228 return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
229 }
230
231
232 /** @return a string representation of the contained instruction.
233 */
234 @Override
235 public String toString() {
236 return toString(true);
237 }
238
239
240 /** Add an attribute to an instruction handle.
241 *
242 * @param key the key object to store/retrieve the attribute
243 * @param attr the attribute to associate with this handle
244 */
245 public void addAttribute( Object key, Object attr ) {
246 if (attributes == null) {
247 attributes = new HashMap<Object, Object>(3);
248 }
249 attributes.put(key, attr);
250 }
251
252
253 /** Delete an attribute of an instruction handle.
254 *
255 * @param key the key object to retrieve the attribute
256 */
257 public void removeAttribute( Object key ) {
258 if (attributes != null) {
259 attributes.remove(key);
260 }
261 }
262
263
264 /** Get attribute of an instruction handle.
265 *
266 * @param key the key object to store/retrieve the attribute
267 */
268 public Object getAttribute( Object key ) {
269 if (attributes != null) {
270 return attributes.get(key);
271 }
272 return null;
273 }
274
275
276 /** @return all attributes associated with this handle
277 */
278 public Collection<Object> getAttributes() {
279 if (attributes == null) {
280 attributes = new HashMap<Object, Object>(3);
281 }
282 return attributes.values();
283 }
284
285
286 /** Convenience method, simply calls accept() on the contained instruction.
287 *
288 * @param v Visitor object
289 */
290 public void accept( Visitor v ) {
291 instruction.accept(v);
292 }
293 }