View Javadoc
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 }