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   * @see Instruction
41   * @see BranchHandle
42   * @see InstructionList
43   */
44  public class InstructionHandle {
45  
46      private InstructionHandle next;
47      private InstructionHandle prev;
48      private Instruction instruction;
49  
50      /**
51       * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
52       */
53      @Deprecated
54      protected int i_position = -1; // byte code offset of instruction
55  
56      private Set<InstructionTargeter> targeters;
57      private Map<Object, Object> attributes;
58  
59  
60      /**
61       * Does nothing.
62       *
63       * @deprecated Does nothing as of 6.3.1.
64       */
65      @Deprecated
66      protected void addHandle() {
67          // noop
68      }
69  
70      public final InstructionHandle getNext() {
71          return next;
72      }
73  
74  
75      public final InstructionHandle getPrev() {
76          return prev;
77      }
78  
79  
80      public final Instruction getInstruction() {
81          return instruction;
82      }
83  
84  
85      /**
86       * Replace current instruction contained in this handle.
87       * Old instruction is disposed using Instruction.dispose().
88       */
89      public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected?
90          if (i == null) {
91              throw new ClassGenException("Assigning null to handle");
92          }
93          if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
94              throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
95          }
96          if (instruction != null) {
97              instruction.dispose();
98          }
99          instruction = i;
100     }
101 
102 
103     /**
104      * Temporarily swap the current instruction, without disturbing
105      * anything. Meant to be used by a debugger, implementing
106      * breakpoints. Current instruction is returned.
107      * <p>
108      * Warning: if this is used on a BranchHandle then some methods such as
109      * getPosition() will still refer to the original cached instruction, whereas
110      * other BH methods may affect the cache and the replacement instruction.
111      */
112     // See BCEL-273
113     // TODO remove this method in any redesign of BCEL
114     public Instructionstruction">Instruction swapInstruction( final Instruction i ) {
115         final Instruction oldInstruction = instruction;
116         instruction = i;
117         return oldInstruction;
118     }
119 
120 
121     /*private*/protected InstructionHandle(final Instruction i) {
122         setInstruction(i);
123     }
124 
125     /** Factory method.
126      */
127     static InstructionHandle getInstructionHandle( final Instruction i ) {
128         return new InstructionHandle(i);
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     /**
166      * Delete contents, i.e., remove user access.
167      */
168     void dispose() {
169         next = prev = null;
170         instruction.dispose();
171         instruction = null;
172         i_position = -1;
173         attributes = null;
174         removeAllTargeters();
175     }
176 
177 
178     /** Remove all targeters, if any.
179      */
180     public void removeAllTargeters() {
181         if (targeters != null) {
182             targeters.clear();
183         }
184     }
185 
186 
187     /**
188      * Denote this handle isn't referenced anymore by t.
189      */
190     public void removeTargeter( final InstructionTargeter t ) {
191         if (targeters != null) {
192             targeters.remove(t);
193         }
194     }
195 
196 
197     /**
198      * Denote this handle is being referenced by t.
199      */
200     public void addTargeter( final InstructionTargeter t ) {
201         if (targeters == null) {
202             targeters = new HashSet<>();
203         }
204         //if(!targeters.contains(t))
205         targeters.add(t);
206     }
207 
208 
209     public boolean hasTargeters() {
210         return (targeters != null) && (targeters.size() > 0);
211     }
212 
213 
214     /**
215      * @return null, if there are no targeters
216      */
217     public InstructionTargeter[] getTargeters() {
218         if (!hasTargeters()) {
219             return new InstructionTargeter[0];
220         }
221         final InstructionTargetergeter.html#InstructionTargeter">InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
222         targeters.toArray(t);
223         return t;
224     }
225 
226 
227     /** @return a (verbose) string representation of the contained instruction.
228      */
229     public String toString( final boolean verbose ) {
230         return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
231     }
232 
233 
234     /** @return a string representation of the contained instruction.
235      */
236     @Override
237     public String toString() {
238         return toString(true);
239     }
240 
241 
242     /** Add an attribute to an instruction handle.
243      *
244      * @param key the key object to store/retrieve the attribute
245      * @param attr the attribute to associate with this handle
246      */
247     public void addAttribute( final Object key, final Object attr ) {
248         if (attributes == null) {
249             attributes = new HashMap<>(3);
250         }
251         attributes.put(key, attr);
252     }
253 
254 
255     /** Delete an attribute of an instruction handle.
256      *
257      * @param key the key object to retrieve the attribute
258      */
259     public void removeAttribute( final Object key ) {
260         if (attributes != null) {
261             attributes.remove(key);
262         }
263     }
264 
265 
266     /** Get attribute of an instruction handle.
267      *
268      * @param key the key object to store/retrieve the attribute
269      */
270     public Object getAttribute( final Object key ) {
271         if (attributes != null) {
272             return attributes.get(key);
273         }
274         return null;
275     }
276 
277 
278     /** @return all attributes associated with this handle
279      */
280     public Collection<Object> getAttributes() {
281         if (attributes == null) {
282             attributes = new HashMap<>(3);
283         }
284         return attributes.values();
285     }
286 
287 
288     /** Convenience method, simply calls accept() on the contained instruction.
289      *
290      * @param v Visitor object
291      */
292     public void accept( final Visitor v ) {
293         instruction.accept(v);
294     }
295 
296 
297     /**
298      * @param next the next to set
299      * @since 6.0
300      */
301     final InstructionHandlee.html#InstructionHandle">InstructionHandle setNext(final InstructionHandle next) {
302         this.next = next;
303         return next;
304     }
305 
306 
307     /**
308      * @param prev the prev to set
309      * @since 6.0
310      */
311     final InstructionHandlee.html#InstructionHandle">InstructionHandle setPrev(final InstructionHandle prev) {
312         this.prev = prev;
313         return prev;
314     }
315 }