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     * Gets 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        return attributes != null ? attributes.get(key) : null;
140    }
141
142    /**
143     * @return all attributes associated with this handle
144     */
145    public Collection<Object> getAttributes() {
146        if (attributes == null) {
147            attributes = new HashMap<>(3);
148        }
149        return attributes.values();
150    }
151
152    public final Instruction getInstruction() {
153        return instruction;
154    }
155
156    public final InstructionHandle getNext() {
157        return next;
158    }
159
160    /**
161     * @return the position, i.e., the byte code offset of the contained instruction. This is accurate only after
162     *         InstructionList.setPositions() has been called.
163     */
164    public int getPosition() {
165        return i_position;
166    }
167
168    public final InstructionHandle getPrev() {
169        return prev;
170    }
171
172    /**
173     * @return null, if there are no targeters
174     */
175    public InstructionTargeter[] getTargeters() {
176        if (!hasTargeters()) {
177            return EMPTY_INSTRUCTION_TARGETER_ARRAY;
178        }
179        final InstructionTargeter[] t = new InstructionTargeter[targeters.size()];
180        targeters.toArray(t);
181        return t;
182    }
183
184    public boolean hasTargeters() {
185        return targeters != null && !targeters.isEmpty();
186    }
187
188    /**
189     * Remove all targeters, if any.
190     */
191    public void removeAllTargeters() {
192        if (targeters != null) {
193            targeters.clear();
194        }
195    }
196
197    /**
198     * Delete an attribute of an instruction handle.
199     *
200     * @param key the key object to retrieve the attribute
201     */
202    public void removeAttribute(final Object key) {
203        if (attributes != null) {
204            attributes.remove(key);
205        }
206    }
207
208    /**
209     * Denote this handle isn't referenced anymore by t.
210     */
211    public void removeTargeter(final InstructionTargeter t) {
212        if (targeters != null) {
213            targeters.remove(t);
214        }
215    }
216
217    /**
218     * Replace current instruction contained in this handle. Old instruction is disposed using Instruction.dispose().
219     */
220    public void setInstruction(final Instruction i) { // Overridden in BranchHandle TODO could be package-protected?
221        if (i == null) {
222            throw new ClassGenException("Assigning null to handle");
223        }
224        if (this.getClass() != BranchHandle.class && i instanceof BranchInstruction) {
225            throw new ClassGenException("Assigning branch instruction " + i + " to plain handle");
226        }
227        if (instruction != null) {
228            instruction.dispose();
229        }
230        instruction = i;
231    }
232
233    /**
234     * @param next the next to set
235     * @since 6.0
236     */
237    final InstructionHandle setNext(final InstructionHandle next) {
238        this.next = next;
239        return next;
240    }
241
242    /**
243     * Sets the position, i.e., the byte code offset of the contained instruction.
244     */
245    void setPosition(final int pos) {
246        i_position = pos;
247    }
248
249    /**
250     * @param prev the prev to set
251     * @since 6.0
252     */
253    final InstructionHandle setPrev(final InstructionHandle prev) {
254        this.prev = prev;
255        return prev;
256    }
257
258    /**
259     * Temporarily swap the current instruction, without disturbing anything. Meant to be used by a debugger, implementing
260     * breakpoints. Current instruction is returned.
261     * <p>
262     * Warning: if this is used on a BranchHandle then some methods such as getPosition() will still refer to the original
263     * cached instruction, whereas other BH methods may affect the cache and the replacement instruction.
264     */
265    // See BCEL-273
266    // TODO remove this method in any redesign of BCEL
267    public Instruction swapInstruction(final Instruction i) {
268        final Instruction oldInstruction = instruction;
269        instruction = i;
270        return oldInstruction;
271    }
272
273    /**
274     * @return a string representation of the contained instruction.
275     */
276    @Override
277    public String toString() {
278        return toString(true);
279    }
280
281    /**
282     * @return a (verbose) string representation of the contained instruction.
283     */
284    public String toString(final boolean verbose) {
285        return Utility.format(i_position, 4, false, ' ') + ": " + instruction.toString(verbose);
286    }
287
288    /**
289     * Called by InstructionList.setPositions when setting the position for every instruction. In the presence of variable
290     * length instructions 'setPositions()' performs multiple passes over the instruction list to calculate the correct
291     * (byte) positions and offsets by calling this function.
292     *
293     * @param offset additional offset caused by preceding (variable length) instructions
294     * @param maxOffset the maximum offset that may be caused by these instructions
295     * @return additional offset caused by possible change of this instruction's length
296     */
297    protected int updatePosition(final int offset, final int maxOffset) {
298        i_position += offset;
299        return 0;
300    }
301}