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