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