    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}