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