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