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