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