001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.bcel.generic;
019
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.Map;
024import java.util.Set;
025
026import org.apache.bcel.classfile.Utility;
027
028/**
029 * Instances of this class give users a handle to the instructions contained in
030 * an InstructionList. Instruction objects may be used more than once within a
031 * list, this is useful because it saves memory and may be much faster.
032 *
033 * Within an InstructionList an InstructionHandle object is wrapped
034 * around all instructions, i.e., it implements a cell in a
035 * doubly-linked list. From the outside only the next and the
036 * previous instruction (handle) are accessible. One
037 * can traverse the list via an Enumeration returned by
038 * InstructionList.elements().
039 *
040 * @version $Id: InstructionHandle.java 1749603 2016-06-21 20:50:19Z ggregory $
041 * @see Instruction
042 * @see BranchHandle
043 * @see InstructionList 
044 */
045public class InstructionHandle {
046
047    private InstructionHandle next;
048    private InstructionHandle prev;
049    private Instruction instruction;
050
051    /**
052     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
053     */
054    @Deprecated
055    protected int i_position = -1; // byte code offset of instruction
056
057    private Set<InstructionTargeter> targeters;
058    private Map<Object, Object> attributes;
059
060
061    public final InstructionHandle getNext() {
062        return next;
063    }
064
065
066    public final InstructionHandle getPrev() {
067        return prev;
068    }
069
070
071    public final Instruction getInstruction() {
072        return instruction;
073    }
074
075
076    /**
077     * Replace current instruction contained in this handle.
078     * Old instruction is disposed using Instruction.dispose().
079     */
080    public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected?
081        if (i == null) {
082            throw new ClassGenException("Assigning null to handle");
083        }
084        if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
085            throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
086        }
087        if (instruction != null) {
088            instruction.dispose();
089        }
090        instruction = i;
091    }
092
093
094    /**
095     * Temporarily swap the current instruction, without disturbing
096     * anything. Meant to be used by a debugger, implementing
097     * breakpoints. Current instruction is returned.
098     * <p>
099     * Warning: if this is used on a BranchHandle then some methods such as
100     * getPosition() will still refer to the original cached instruction, whereas
101     * other BH methods may affect the cache and the replacement instruction.
102     */
103    // See BCEL-273
104    // TODO remove this method in any redesign of BCEL
105    public Instruction swapInstruction( final Instruction i ) {
106        final Instruction oldInstruction = instruction;
107        instruction = i;
108        return oldInstruction;
109    }
110
111
112    /*private*/protected InstructionHandle(final Instruction i) {
113        setInstruction(i);
114    }
115
116    private static InstructionHandle ih_list = null; // List of reusable handles
117
118
119    /** Factory method.
120     */
121    static InstructionHandle getInstructionHandle( final Instruction i ) {
122        if (ih_list == null) {
123            return new InstructionHandle(i);
124        }
125        final InstructionHandle ih = ih_list;
126        ih_list = ih.next;
127        ih.setInstruction(i);
128        return ih;
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    /** Overridden in BranchHandle
166     */
167    protected void addHandle() {
168        next = ih_list;
169        ih_list = this;
170    }
171
172
173    /**
174     * Delete contents, i.e., remove user access and make handle reusable.
175     */
176    void dispose() {
177        next = prev = null;
178        instruction.dispose();
179        instruction = null;
180        i_position = -1;
181        attributes = null;
182        removeAllTargeters();
183        addHandle();
184    }
185
186
187    /** Remove all targeters, if any.
188     */
189    public void removeAllTargeters() {
190        if (targeters != null) {
191            targeters.clear();
192        }
193    }
194
195
196    /**
197     * Denote this handle isn't referenced anymore by t.
198     */
199    public void removeTargeter( final InstructionTargeter t ) {
200        if (targeters != null) {
201            targeters.remove(t);
202        }
203    }
204
205
206    /**
207     * Denote this handle is being referenced by t.
208     */
209    public void addTargeter( final InstructionTargeter t ) {
210        if (targeters == null) {
211            targeters = new HashSet<>();
212        }
213        //if(!targeters.contains(t))
214        targeters.add(t);
215    }
216
217
218    public boolean hasTargeters() {
219        return (targeters != null) && (targeters.size() > 0);
220    }
221
222
223    /**
224     * @return null, if there are no targeters
225     */
226    public InstructionTargeter[] getTargeters() {
227        if (!hasTargeters()) {
228            return new InstructionTargeter[0];
229        }
230        final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
231        targeters.toArray(t);
232        return t;
233    }
234
235
236    /** @return a (verbose) string representation of the contained instruction. 
237     */
238    public String toString( final boolean verbose ) {
239        return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
240    }
241
242
243    /** @return a string representation of the contained instruction. 
244     */
245    @Override
246    public String toString() {
247        return toString(true);
248    }
249
250
251    /** Add an attribute to an instruction handle.
252     *
253     * @param key the key object to store/retrieve the attribute
254     * @param attr the attribute to associate with this handle
255     */
256    public void addAttribute( final Object key, final Object attr ) {
257        if (attributes == null) {
258            attributes = new HashMap<>(3);
259        }
260        attributes.put(key, attr);
261    }
262
263
264    /** Delete an attribute of an instruction handle.
265     *
266     * @param key the key object to retrieve the attribute
267     */
268    public void removeAttribute( final Object key ) {
269        if (attributes != null) {
270            attributes.remove(key);
271        }
272    }
273
274
275    /** Get attribute of an instruction handle.
276     *
277     * @param key the key object to store/retrieve the attribute
278     */
279    public Object getAttribute( final Object key ) {
280        if (attributes != null) {
281            return attributes.get(key);
282        }
283        return null;
284    }
285
286
287    /** @return all attributes associated with this handle
288     */
289    public Collection<Object> getAttributes() {
290        if (attributes == null) {
291            attributes = new HashMap<>(3);
292        }
293        return attributes.values();
294    }
295
296
297    /** Convenience method, simply calls accept() on the contained instruction.
298     *
299     * @param v Visitor object
300     */
301    public void accept( final Visitor v ) {
302        instruction.accept(v);
303    }
304
305
306    /**
307     * @param next the next to set
308     * @ since 6.0
309     */
310    final InstructionHandle setNext(final InstructionHandle next) {
311        this.next = next;
312        return next;
313    }
314
315
316    /**
317     * @param prev the prev to set
318     * @ since 6.0
319     */
320    final InstructionHandle setPrev(final InstructionHandle prev) {
321        this.prev = prev;
322        return prev;
323    }
324}