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