View Javadoc
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: InstructionHandle.html 992795 2016-07-14 11:53:52Z britter $
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  
61      public final InstructionHandle getNext() {
62          return next;
63      }
64  
65  
66      public final InstructionHandle getPrev() {
67          return prev;
68      }
69  
70  
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       */
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
105     public Instruction swapInstruction( final Instruction i ) {
106         final Instruction oldInstruction = instruction;
107         instruction = i;
108         return oldInstruction;
109     }
110 
111 
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      */
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      */
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      */
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      */
160     void setPosition( final int pos ) {
161         i_position = pos;
162     }
163 
164 
165     /** Overridden in BranchHandle
166      */
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      */
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      */
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      */
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      */
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 
218     public boolean hasTargeters() {
219         return (targeters != null) && (targeters.size() > 0);
220     }
221 
222 
223     /**
224      * @return null, if there are no targeters
225      */
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      */
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
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      */
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      */
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      */
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      */
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      */
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      */
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      */
320     final InstructionHandle setPrev(final InstructionHandle prev) {
321         this.prev = prev;
322         return prev;
323     }
324 }