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: LocalVariableGen.java 1749603 2016-06-21 20:50:19Z ggregory $
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
041
042    /**
043     * Generate a local variable that with index `index'. Note that double and long
044     * variables need two indexs. Index indices have to be provided by the user.
045     *
046     * @param index index of local variable
047     * @param name its name
048     * @param type its type
049     * @param start from where the instruction is valid (null means from the start)
050     * @param end until where the instruction is valid (null means to the end)
051     */
052    public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start,
053            final InstructionHandle end) {
054        if ((index < 0) || (index > Const.MAX_SHORT)) {
055            throw new ClassGenException("Invalid index index: " + index);
056        }
057        this.name = name;
058        this.type = type;
059        this.index = index;
060        setStart(start);
061        setEnd(end);
062    }
063
064
065    /**
066     * Get LocalVariable object.
067     *
068     * This relies on that the instruction list has already been dumped to byte code or
069     * or that the `setPositions' methods has been called for the instruction list.
070     *
071     * Note that for local variables whose scope end at the last
072     * instruction of the method's code, the JVM specification is ambiguous:
073     * both a start_pc+length ending at the last instruction and
074     * start_pc+length ending at first index beyond the end of the code are
075     * valid.
076     *
077     * @param cp constant pool
078     */
079    public LocalVariable getLocalVariable( final ConstantPoolGen cp ) {
080        int start_pc = 0;
081        int length = 0;
082        if ((start != null) && (end != null)) {
083            start_pc = start.getPosition();
084            length = end.getPosition() - start_pc;
085            if (end.getNext() == null) {
086                length += end.getInstruction().getLength();
087            }
088        }
089        final int name_index = cp.addUtf8(name);
090        final int signature_index = cp.addUtf8(type.getSignature());
091        return new LocalVariable(start_pc, length, name_index, signature_index, index, cp
092                .getConstantPool());
093    }
094
095
096    public void setIndex( final int index ) {
097        this.index = index;
098    }
099
100
101    public int getIndex() {
102        return index;
103    }
104
105
106    @Override
107    public void setName( final String name ) {
108        this.name = name;
109    }
110
111
112    @Override
113    public String getName() {
114        return name;
115    }
116
117
118    @Override
119    public void setType( final Type type ) {
120        this.type = type;
121    }
122
123
124    @Override
125    public Type getType() {
126        return type;
127    }
128
129
130    public InstructionHandle getStart() {
131        return start;
132    }
133
134
135    public InstructionHandle getEnd() {
136        return end;
137    }
138
139
140    public void setStart( final InstructionHandle start ) { // TODO could be package-protected?
141        BranchInstruction.notifyTarget(this.start, start, this);
142        this.start = start;
143    }
144
145
146    public void setEnd( final InstructionHandle end ) { // TODO could be package-protected?
147        BranchInstruction.notifyTarget(this.end, end, this);
148        this.end = end;
149    }
150
151
152    /**
153     * @param old_ih old target, either start or end
154     * @param new_ih new target
155     */
156    @Override
157    public void updateTarget( final InstructionHandle old_ih, final InstructionHandle new_ih ) {
158        boolean targeted = false;
159        if (start == old_ih) {
160            targeted = true;
161            setStart(new_ih);
162        }
163        if (end == old_ih) {
164            targeted = true;
165            setEnd(new_ih);
166        }
167        if (!targeted) {
168            throw new ClassGenException("Not targeting " + old_ih + ", but {" + start + ", " + end
169                    + "}");
170        }
171    }
172
173    /**
174     * Clear the references from and to this variable when it's removed.
175     */
176    void dispose() {
177        setStart(null);
178        setEnd(null);
179    }
180
181    /**
182     * @return true, if ih is target of this variable
183     */
184    @Override
185    public boolean containsTarget( final InstructionHandle ih ) {
186        return (start == ih) || (end == ih);
187    }
188
189
190    @Override
191    public int hashCode() {
192        // If the user changes the name or type, problems with the targeter hashmap will occur.
193        // Note: index cannot be part of hash as it may be changed by the user.
194        return name.hashCode() ^ type.hashCode();
195    }
196
197
198    /**
199     * We consider to local variables to be equal, if the use the same index and
200     * are valid in the same range.
201     */
202    @Override
203    public boolean equals( final Object o ) {
204        if (!(o instanceof LocalVariableGen)) {
205            return false;
206        }
207        final LocalVariableGen l = (LocalVariableGen) o;
208        return (l.index == index) && (l.start == start) && (l.end == end);
209    }
210
211
212    @Override
213    public String toString() {
214        return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
215    }
216
217
218    @Override
219    public Object clone() {
220        try {
221            return super.clone();
222        } catch (final CloneNotSupportedException e) {
223            throw new Error("Clone Not Supported"); // never happens
224        }
225    }
226}