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 * @see Instruction
041 * @see BranchHandle
042 * @see InstructionList
043 */
044public class InstructionHandle {
045
046    private InstructionHandle next;
047    private InstructionHandle prev;
048    private Instruction instruction;
049
050    /**
051     * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter
052     */
053    @Deprecated
054    protected int i_position = -1; // byte code offset of instruction
055
056    private Set<InstructionTargeter> targeters;
057    private Map<Object, Object> attributes;
058
059
060    /**
061     * Does nothing.
062     *
063     * @deprecated Does nothing as of 6.3.1.
064     */
065    @Deprecated
066    protected void addHandle() {
067        // noop
068    }
069
070    public final InstructionHandle getNext() {
071        return next;
072    }
073
074
075    public final InstructionHandle getPrev() {
076        return prev;
077    }
078
079
080    public final Instruction getInstruction() {
081        return instruction;
082    }
083
084
085    /**
086     * Replace current instruction contained in this handle.
087     * Old instruction is disposed using Instruction.dispose().
088     */
089    public void setInstruction( final Instruction i ) { // Overridden in BranchHandle TODO could be package-protected?
090        if (i == null) {
091            throw new ClassGenException("Assigning null to handle");
092        }
093        if ((this.getClass() != BranchHandle.class) && (i instanceof BranchInstruction)) {
094            throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
095        }
096        if (instruction != null) {
097            instruction.dispose();
098        }
099        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 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 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 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 InstructionHandle setPrev(final InstructionHandle prev) {
312        this.prev = prev;
313        return prev;
314    }
315}