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