    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}