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