    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 org.apache.bcel.Const;
    021import org.apache.bcel.classfile.LocalVariable;
    022
    023/**
    024 * Represents a local variable within a method. It contains its scope, name and type. The generated LocalVariable object
    025 * can be obtained with getLocalVariable which needs the instruction list and the constant pool as parameters.
    026 *
    027 * @see LocalVariable
    028 * @see MethodGen
    029 */
    030public class LocalVariableGen implements InstructionTargeter, NamedAndTyped, Cloneable {
    031
    032    private int index;
    033    private String name;
    034    private Type type;
    035    private InstructionHandle start;
    036    private InstructionHandle end;
    037    private int origIndex; // never changes; used to match up with LocalVariableTypeTable entries
    038    private boolean liveToEnd;
    039
    040    /**
    041     * Generate a local variable that with index `index'. Note that double and long variables need two indexs. Index indices
    042     * have to be provided by the user.
    043     *
    044     * @param index index of local variable
    045     * @param name its name
    046     * @param type its type
    047     * @param start from where the instruction is valid (null means from the start)
    048     * @param end until where the instruction is valid (null means to the end)
    049     */
    050    public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end) {
    051        if (index < 0 || index > Const.MAX_SHORT) {
    052            throw new ClassGenException("Invalid index: " + index);
    053        }
    054        this.name = name;
    055        this.type = type;
    056        this.index = index;
    057        setStart(start);
    058        setEnd(end);
    059        this.origIndex = index;
    060        this.liveToEnd = end == null;
    061    }
    062
    063    /**
    064     * Generates a local variable that with index `index'. Note that double and long variables need two indexs. Index
    065     * indices have to be provided by the user.
    066     *
    067     * @param index index of local variable
    068     * @param name its name
    069     * @param type its type
    070     * @param start from where the instruction is valid (null means from the start)
    071     * @param end until where the instruction is valid (null means to the end)
    072     * @param origIndex index of local variable prior to any changes to index
    073     */
    074    public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end,
    075        final int origIndex) {
    076        this(index, name, type, start, end);
    077        this.origIndex = origIndex;
    078    }
    079
    080    @Override
    081    public Object clone() {
    082        try {
    083            return super.clone();
    084        } catch (final CloneNotSupportedException e) {
    085            throw new Error("Clone Not Supported"); // never happens
    086        }
    087    }
    088
    089    /**
    090     * @return true, if ih is target of this variable
    091     */
    092    @Override
    093    public boolean containsTarget(final InstructionHandle ih) {
    094        return start == ih || end == ih;
    095    }
    096
    097    /**
    098     * Clear the references from and to this variable when it's removed.
    099     */
    100    void dispose() {
    101        setStart(null);
    102        setEnd(null);
    103    }
    104
    105    /**
    106     * We consider to local variables to be equal, if the use the same index and are valid in the same range.
    107     */
    108    @Override
    109    public boolean equals(final Object o) {
    110        if (!(o instanceof LocalVariableGen)) {
    111            return false;
    112        }
    113        final LocalVariableGen l = (LocalVariableGen) o;
    114        return l.index == index && l.start == start && l.end == end;
    115    }
    116
    117    public InstructionHandle getEnd() {
    118        return end;
    119    }
    120
    121    public int getIndex() {
    122        return index;
    123    }
    124
    125    public boolean getLiveToEnd() {
    126        return liveToEnd;
    127    }
    128
    129    /**
    130     * Gets LocalVariable object.
    131     *
    132     * This relies on that the instruction list has already been dumped to byte code or that the `setPositions' methods
    133     * has been called for the instruction list.
    134     *
    135     * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference
    136     * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last
    137     * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases.
    138     *
    139     * @param cp constant pool
    140     */
    141    public LocalVariable getLocalVariable(final ConstantPoolGen cp) {
    142        int start_pc = 0;
    143        int length = 0;
    144        if (start != null && end != null) {
    145            start_pc = start.getPosition();
    146            length = end.getPosition() - start_pc;
    147            if (end.getNext() == null && liveToEnd) {
    148                length += end.getInstruction().getLength();
    149            }
    150        }
    151        final int name_index = cp.addUtf8(name);
    152        final int signature_index = cp.addUtf8(type.getSignature());
    153        return new LocalVariable(start_pc, length, name_index, signature_index, index, cp.getConstantPool(), origIndex);
    154    }
    155
    156    @Override
    157    public String getName() {
    158        return name;
    159    }
    160
    161    public int getOrigIndex() {
    162        return origIndex;
    163    }
    164
    165    public InstructionHandle getStart() {
    166        return start;
    167    }
    168
    169    @Override
    170    public Type getType() {
    171        return type;
    172    }
    173
    174    @Override
    175    public int hashCode() {
    176        // If the user changes the name or type, problems with the targeter hashmap will occur.
    177        // Note: index cannot be part of hash as it may be changed by the user.
    178        return name.hashCode() ^ type.hashCode();
    179    }
    180
    181    public void setEnd(final InstructionHandle end) { // TODO could be package-protected?
    182        BranchInstruction.notifyTarget(this.end, end, this);
    183        this.end = end;
    184    }
    185
    186    public void setIndex(final int index) {
    187        this.index = index;
    188    }
    189
    190    public void setLiveToEnd(final boolean live_to_end) {
    191        this.liveToEnd = live_to_end;
    192    }
    193
    194    @Override
    195    public void setName(final String name) {
    196        this.name = name;
    197    }
    198
    199    public void setStart(final InstructionHandle start) { // TODO could be package-protected?
    200        BranchInstruction.notifyTarget(this.start, start, this);
    201        this.start = start;
    202    }
    203
    204    @Override
    205    public void setType(final Type type) {
    206        this.type = type;
    207    }
    208
    209    @Override
    210    public String toString() {
    211        return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
    212    }
    213
    214    /**
    215     * @param old_ih old target, either start or end
    216     * @param new_ih new target
    217     */
    218    @Override
    219    public void updateTarget(final InstructionHandle old_ih, final InstructionHandle new_ih) {
    220        boolean targeted = false;
    221        if (start == old_ih) {
    222            targeted = true;
    223            setStart(new_ih);
    224        }
    225        if (end == old_ih) {
    226            targeted = true;
    227            setEnd(new_ih);
    228        }
    229        if (!targeted) {
    230            throw new ClassGenException("Not targeting " + old_ih + ", but {" + start + ", " + end + "}");
    231        }
    232    }
    233}