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