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}