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