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