    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    public InstructionHandle getEnd() {
    119        return end;
    120    }
    121
    122    public int getIndex() {
    123        return index;
    124    }
    125
    126    public boolean getLiveToEnd() {
    127        return liveToEnd;
    128    }
    129
    130    /**
    131     * Gets LocalVariable object.
    132     *
    133     * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods
    134     * has been called for the instruction list.
    135     *
    136     * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference
    137     * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last
    138     * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases.
    139     *
    140     * @param cp constant pool
    141     */
    142    public LocalVariable getLocalVariable(final ConstantPoolGen cp) {
    143        int startPc = 0;
    144        int length = 0;
    145        if (start != null && end != null) {
    146            startPc = start.getPosition();
    147            length = end.getPosition() - startPc;
    148            if (end.getNext() == null && liveToEnd) {
    149                length += end.getInstruction().getLength();
    150            }
    151        }
    152        final int nameIndex = cp.addUtf8(name);
    153        final int signatureIndex = cp.addUtf8(type.getSignature());
    154        return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex);
    155    }
    156
    157    @Override
    158    public String getName() {
    159        return name;
    160    }
    161
    162    public int getOrigIndex() {
    163        return origIndex;
    164    }
    165
    166    public InstructionHandle getStart() {
    167        return start;
    168    }
    169
    170    @Override
    171    public Type getType() {
    172        return type;
    173    }
    174
    175    @Override
    176    public int hashCode() {
    177        // If the user changes the name or type, problems with the targeter hashmap will occur.
    178        // Note: Index cannot be part of hash as it may be changed by the user.
    179        return name.hashCode() ^ type.hashCode();
    180    }
    181
    182    public void setEnd(final InstructionHandle end) { // TODO could be package-protected?
    183        BranchInstruction.notifyTarget(this.end, end, this);
    184        this.end = end;
    185    }
    186
    187    public void setIndex(final int index) {
    188        this.index = index;
    189    }
    190
    191    public void setLiveToEnd(final boolean liveToEnd) {
    192        this.liveToEnd = liveToEnd;
    193    }
    194
    195    @Override
    196    public void setName(final String name) {
    197        this.name = name;
    198    }
    199
    200    public void setStart(final InstructionHandle start) { // TODO could be package-protected?
    201        BranchInstruction.notifyTarget(this.start, start, this);
    202        this.start = start;
    203    }
    204
    205    @Override
    206    public void setType(final Type type) {
    207        this.type = type;
    208    }
    209
    210    @Override
    211    public String toString() {
    212        return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
    213    }
    214
    215    /**
    216     * @param oldIh old target, either start or end
    217     * @param newIh new target
    218     */
    219    @Override
    220    public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
    221        boolean targeted = false;
    222        if (start == oldIh) {
    223            targeted = true;
    224            setStart(newIh);
    225        }
    226        if (end == oldIh) {
    227            targeted = true;
    228            setEnd(newIh);
    229        }
    230        if (!targeted) {
    231            throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}");
    232        }
    233    }
    234}