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