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 
26 import org.apache.bcel.classfile.Utility;
27 
28 /**
29  * Instances of this class give users a handle to the instructions contained in
30  * an InstructionList. Instruction objects may be used more than once within a
31  * list, this is useful because it saves memory and may be much faster.
32  *
33  * Within an InstructionList an InstructionHandle object is wrapped
34  * around all instructions, i.e., it implements a cell in a
35  * doubly-linked list. From the outside only the next and the
36  * previous instruction (handle) are accessible. One
37  * can traverse the list via an Enumeration returned by
38  * InstructionList.elements().
39  *
40  * @version $Id$
41  * @see Instruction
42  * @see BranchHandle
43  * @see InstructionList
44  */
45 public class InstructionHandle {
46 
47     private InstructionHandle next;
48     private InstructionHandle prev;
49     private Instruction instruction;
50 
51     /**
52      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
53      */
54     @Deprecated
55     protected int i_position = -1; // byte code offset of instruction
56 
57     private Set<InstructionTargeter> targeters;
58     private Map<Object, Object> attributes;
59 
60 
getNext()61     public final InstructionHandle getNext() {
62         return next;
63     }
64 
65 
getPrev()66     public final InstructionHandle getPrev() {
67         return prev;
68     }
69 
70 
getInstruction()71     public final Instruction getInstruction() {
72         return instruction;
73     }
74 
75 
76     /**
77      * Replace current instruction contained in this handle.
78      * Old instruction is disposed using Instruction.dispose().
79      */
setInstruction( final Instruction i )80     public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected?
81         if (i == null) {
82             throw new ClassGenException("Assigning null to handle");
83         }
84         if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
85             throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
86         }
87         if (instruction != null) {
88             instruction.dispose();
89         }
90         instruction = i;
91     }
92 
93 
94     /**
95      * Temporarily swap the current instruction, without disturbing
96      * anything. Meant to be used by a debugger, implementing
97      * breakpoints. Current instruction is returned.
98      * <p>
99      * Warning: if this is used on a BranchHandle then some methods such as
100      * getPosition() will still refer to the original cached instruction, whereas
101      * other BH methods may affect the cache and the replacement instruction.
102      */
103     // See BCEL-273
104     // TODO remove this method in any redesign of BCEL
swapInstruction( final Instruction i )105     public Instruction swapInstruction( final Instruction i ) {
106         final Instruction oldInstruction = instruction;
107         instruction = i;
108         return oldInstruction;
109     }
110 
111 
InstructionHandle(final Instruction i)112     /*private*/protected InstructionHandle(final Instruction i) {
113         setInstruction(i);
114     }
115 
116     private static InstructionHandle ih_list = null; // List of reusable handles
117 
118 
119     /** Factory method.
120      */
getInstructionHandle( final Instruction i )121     static InstructionHandle getInstructionHandle( final Instruction i ) {
122         if (ih_list == null) {
123             return new InstructionHandle(i);
124         }
125         final InstructionHandle ih = ih_list;
126         ih_list = ih.next;
127         ih.setInstruction(i);
128         return ih;
129     }
130 
131 
132     /**
133      * Called by InstructionList.setPositions when setting the position for every
134      * instruction. In the presence of variable length instructions `setPositions()'
135      * performs multiple passes over the instruction list to calculate the
136      * correct (byte) positions and offsets by calling this function.
137      *
138      * @param offset additional offset caused by preceding (variable length) instructions
139      * @param max_offset the maximum offset that may be caused by these instructions
140      * @return additional offset caused by possible change of this instruction's length
141      */
updatePosition( final int offset, final int max_offset )142     protected int updatePosition( final int offset, final int max_offset ) {
143         i_position += offset;
144         return 0;
145     }
146 
147 
148     /** @return the position, i.e., the byte code offset of the contained
149      * instruction. This is accurate only after
150      * InstructionList.setPositions() has been called.
151      */
getPosition()152     public int getPosition() {
153         return i_position;
154     }
155 
156 
157     /** Set the position, i.e., the byte code offset of the contained
158      * instruction.
159      */
setPosition( final int pos )160     void setPosition( final int pos ) {
161         i_position = pos;
162     }
163 
164 
165     /** Overridden in BranchHandle
166      */
addHandle()167     protected void addHandle() {
168         next = ih_list;
169         ih_list = this;
170     }
171 
172 
173     /**
174      * Delete contents, i.e., remove user access and make handle reusable.
175      */
dispose()176     void dispose() {
177         next = prev = null;
178         instruction.dispose();
179         instruction = null;
180         i_position = -1;
181         attributes = null;
182         removeAllTargeters();
183         addHandle();
184     }
185 
186 
187     /** Remove all targeters, if any.
188      */
removeAllTargeters()189     public void removeAllTargeters() {
190         if (targeters != null) {
191             targeters.clear();
192         }
193     }
194 
195 
196     /**
197      * Denote this handle isn't referenced anymore by t.
198      */
removeTargeter( final InstructionTargeter t )199     public void removeTargeter( final InstructionTargeter t ) {
200         if (targeters != null) {
201             targeters.remove(t);
202         }
203     }
204 
205 
206     /**
207      * Denote this handle is being referenced by t.
208      */
addTargeter( final InstructionTargeter t )209     public void addTargeter( final InstructionTargeter t ) {
210         if (targeters == null) {
211             targeters = new HashSet<>();
212         }
213         //if(!targeters.contains(t))
214         targeters.add(t);
215     }
216 
217 
hasTargeters()218     public boolean hasTargeters() {
219         return (targeters != null) && (targeters.size() > 0);
220     }
221 
222 
223     /**
224      * @return null, if there are no targeters
225      */
getTargeters()226     public InstructionTargeter[] getTargeters() {
227         if (!hasTargeters()) {
228             return new InstructionTargeter[0];
229         }
230         final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
231         targeters.toArray(t);
232         return t;
233     }
234 
235 
236     /** @return a (verbose) string representation of the contained instruction.
237      */
toString( final boolean verbose )238     public String toString( final boolean verbose ) {
239         return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
240     }
241 
242 
243     /** @return a string representation of the contained instruction.
244      */
245     @Override
toString()246     public String toString() {
247         return toString(true);
248     }
249 
250 
251     /** Add an attribute to an instruction handle.
252      *
253      * @param key the key object to store/retrieve the attribute
254      * @param attr the attribute to associate with this handle
255      */
addAttribute( final Object key, final Object attr )256     public void addAttribute( final Object key, final Object attr ) {
257         if (attributes == null) {
258             attributes = new HashMap<>(3);
259         }
260         attributes.put(key, attr);
261     }
262 
263 
264     /** Delete an attribute of an instruction handle.
265      *
266      * @param key the key object to retrieve the attribute
267      */
removeAttribute( final Object key )268     public void removeAttribute( final Object key ) {
269         if (attributes != null) {
270             attributes.remove(key);
271         }
272     }
273 
274 
275     /** Get attribute of an instruction handle.
276      *
277      * @param key the key object to store/retrieve the attribute
278      */
getAttribute( final Object key )279     public Object getAttribute( final Object key ) {
280         if (attributes != null) {
281             return attributes.get(key);
282         }
283         return null;
284     }
285 
286 
287     /** @return all attributes associated with this handle
288      */
getAttributes()289     public Collection<Object> getAttributes() {
290         if (attributes == null) {
291             attributes = new HashMap<>(3);
292         }
293         return attributes.values();
294     }
295 
296 
297     /** Convenience method, simply calls accept() on the contained instruction.
298      *
299      * @param v Visitor object
300      */
accept( final Visitor v )301     public void accept( final Visitor v ) {
302         instruction.accept(v);
303     }
304 
305 
306     /**
307      * @param next the next to set
308      * @ since 6.0
309      */
setNext(final InstructionHandle next)310     final InstructionHandle setNext(final InstructionHandle next) {
311         this.next = next;
312         return next;
313     }
314 
315 
316     /**
317      * @param prev the prev to set
318      * @ since 6.0
319      */
setPrev(final InstructionHandle prev)320     final InstructionHandle setPrev(final InstructionHandle prev) {
321         this.prev = prev;
322         return prev;
323     }
324 }
325