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