001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.generic;
020
021import org.apache.bcel.Const;
022import org.apache.bcel.classfile.LocalVariable;
023
024/**
025 * Represents a local variable within a method. It contains its scope, name and type. The generated LocalVariable object
026 * can be obtained with getLocalVariable which needs the instruction list and the constant pool as parameters.
027 *
028 * @see LocalVariable
029 * @see MethodGen
030 */
031public class LocalVariableGen implements InstructionTargeter, NamedAndTyped, Cloneable {
032
033    private int index;
034    private String name;
035    private Type type;
036    private InstructionHandle start;
037    private InstructionHandle end;
038    private int origIndex; // never changes; used to match up with LocalVariableTypeTable entries
039    private boolean liveToEnd;
040
041    /**
042     * Generate a local variable that with index 'index'. Note that double and long variables need two indexs. Index indices
043     * have to be provided by the user.
044     *
045     * @param index index of local variable.
046     * @param name its name.
047     * @param type its type.
048     * @param start from where the instruction is valid (null means from the start).
049     * @param end until where the instruction is valid (null means to the end).
050     */
051    public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end) {
052        if (index < 0 || index > Const.MAX_SHORT) {
053            throw new ClassGenException("Invalid index: " + index);
054        }
055        this.name = name;
056        this.type = type;
057        this.index = index;
058        setStart(start);
059        setEnd(end);
060        this.origIndex = index;
061        this.liveToEnd = end == null;
062    }
063
064    /**
065     * Generates a local variable that with index 'index'. Note that double and long variables need two indexs. Index
066     * indices have to be provided by the user.
067     *
068     * @param index index of local variable.
069     * @param name its name.
070     * @param type its type.
071     * @param start from where the instruction is valid (null means from the start).
072     * @param end until where the instruction is valid (null means to the end).
073     * @param origIndex index of local variable prior to any changes to index.
074     */
075    public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end,
076        final int origIndex) {
077        this(index, name, type, start, end);
078        this.origIndex = origIndex;
079    }
080
081    @Override
082    public Object clone() {
083        try {
084            return super.clone();
085        } catch (final CloneNotSupportedException e) {
086            throw new UnsupportedOperationException("Clone Not Supported", e); // never happens
087        }
088    }
089
090    /**
091     * @return true, if ih is target of this variable.
092     */
093    @Override
094    public boolean containsTarget(final InstructionHandle ih) {
095        return start == ih || end == ih;
096    }
097
098    /**
099     * Clear the references from and to this variable when it's removed.
100     */
101    void dispose() {
102        setStart(null);
103        setEnd(null);
104    }
105
106    /**
107     * We consider to local variables to be equal, if the use the same index and are valid in the same range.
108     */
109    @Override
110    public boolean equals(final Object o) {
111        if (!(o instanceof LocalVariableGen)) {
112            return false;
113        }
114        final LocalVariableGen l = (LocalVariableGen) o;
115        return l.index == index && l.start == start && l.end == end;
116    }
117
118    /**
119     * Gets the end instruction handle.
120     *
121     * @return the end instruction handle.
122     */
123    public InstructionHandle getEnd() {
124        return end;
125    }
126
127    /**
128     * Gets the index.
129     *
130     * @return the index.
131     */
132    public int getIndex() {
133        return index;
134    }
135
136    /**
137     * Gets whether the variable lives to the end.
138     *
139     * @return true if the variable lives to the end.
140     */
141    public boolean getLiveToEnd() {
142        return liveToEnd;
143    }
144
145    /**
146     * Gets LocalVariable object.
147     *
148     * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods
149     * has been called for the instruction list.
150     *
151     * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference
152     * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last
153     * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases.
154     *
155     * @param cp constant pool.
156     * @return the local variable.
157     */
158    public LocalVariable getLocalVariable(final ConstantPoolGen cp) {
159        int startPc = 0;
160        int length = 0;
161        if (start != null && end != null) {
162            startPc = start.getPosition();
163            length = end.getPosition() - startPc;
164            if (end.getNext() == null && liveToEnd) {
165                length += end.getInstruction().getLength();
166            }
167        }
168        final int nameIndex = cp.addUtf8(name);
169        final int signatureIndex = cp.addUtf8(type.getSignature());
170        return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex);
171    }
172
173    @Override
174    public String getName() {
175        return name;
176    }
177
178    /**
179     * Gets the original index.
180     *
181     * @return the original index.
182     */
183    public int getOrigIndex() {
184        return origIndex;
185    }
186
187    /**
188     * Gets the start instruction handle.
189     *
190     * @return the start instruction handle.
191     */
192    public InstructionHandle getStart() {
193        return start;
194    }
195
196    @Override
197    public Type getType() {
198        return type;
199    }
200
201    @Override
202    public int hashCode() {
203        // If the user changes the name or type, problems with the targeter hashmap will occur.
204        // Note: Index cannot be part of hash as it may be changed by the user.
205        return name.hashCode() ^ type.hashCode();
206    }
207
208    /**
209     * Sets the end instruction handle.
210     *
211     * @param end the end instruction handle.
212     */
213    public void setEnd(final InstructionHandle end) { // TODO could be package-protected?
214        BranchInstruction.notifyTarget(this.end, end, this);
215        this.end = end;
216    }
217
218    /**
219     * Sets the index.
220     *
221     * @param index the index.
222     */
223    public void setIndex(final int index) {
224        this.index = index;
225    }
226
227    /**
228     * Sets whether the variable lives to the end.
229     *
230     * @param liveToEnd true if the variable lives to the end.
231     */
232    public void setLiveToEnd(final boolean liveToEnd) {
233        this.liveToEnd = liveToEnd;
234    }
235
236    @Override
237    public void setName(final String name) {
238        this.name = name;
239    }
240
241    /**
242     * Sets the start instruction handle.
243     *
244     * @param start the start instruction handle.
245     */
246    public void setStart(final InstructionHandle start) { // TODO could be package-protected?
247        BranchInstruction.notifyTarget(this.start, start, this);
248        this.start = start;
249    }
250
251    @Override
252    public void setType(final Type type) {
253        this.type = type;
254    }
255
256    @Override
257    public String toString() {
258        return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
259    }
260
261    /**
262     * @param oldIh old target, either start or end.
263     * @param newIh new target.
264     */
265    @Override
266    public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
267        boolean targeted = false;
268        if (start == oldIh) {
269            targeted = true;
270            setStart(newIh);
271        }
272        if (end == oldIh) {
273            targeted = true;
274            setEnd(newIh);
275        }
276        if (!targeted) {
277            throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}");
278        }
279    }
280}
281