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