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    public InstructionHandle getEnd() {
119        return end;
120    }
121
122    public int getIndex() {
123        return index;
124    }
125
126    public boolean getLiveToEnd() {
127        return liveToEnd;
128    }
129
130    /**
131     * Gets LocalVariable object.
132     *
133     * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods
134     * has been called for the instruction list.
135     *
136     * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference
137     * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last
138     * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases.
139     *
140     * @param cp constant pool
141     */
142    public LocalVariable getLocalVariable(final ConstantPoolGen cp) {
143        int startPc = 0;
144        int length = 0;
145        if (start != null && end != null) {
146            startPc = start.getPosition();
147            length = end.getPosition() - startPc;
148            if (end.getNext() == null && liveToEnd) {
149                length += end.getInstruction().getLength();
150            }
151        }
152        final int nameIndex = cp.addUtf8(name);
153        final int signatureIndex = cp.addUtf8(type.getSignature());
154        return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex);
155    }
156
157    @Override
158    public String getName() {
159        return name;
160    }
161
162    public int getOrigIndex() {
163        return origIndex;
164    }
165
166    public InstructionHandle getStart() {
167        return start;
168    }
169
170    @Override
171    public Type getType() {
172        return type;
173    }
174
175    @Override
176    public int hashCode() {
177        // If the user changes the name or type, problems with the targeter hashmap will occur.
178        // Note: Index cannot be part of hash as it may be changed by the user.
179        return name.hashCode() ^ type.hashCode();
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    public void setIndex(final int index) {
188        this.index = index;
189    }
190
191    public void setLiveToEnd(final boolean liveToEnd) {
192        this.liveToEnd = liveToEnd;
193    }
194
195    @Override
196    public void setName(final String name) {
197        this.name = name;
198    }
199
200    public void setStart(final InstructionHandle start) { // TODO could be package-protected?
201        BranchInstruction.notifyTarget(this.start, start, this);
202        this.start = start;
203    }
204
205    @Override
206    public void setType(final Type type) {
207        this.type = type;
208    }
209
210    @Override
211    public String toString() {
212        return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
213    }
214
215    /**
216     * @param oldIh old target, either start or end
217     * @param newIh new target
218     */
219    @Override
220    public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
221        boolean targeted = false;
222        if (start == oldIh) {
223            targeted = true;
224            setStart(newIh);
225        }
226        if (end == oldIh) {
227            targeted = true;
228            setEnd(newIh);
229        }
230        if (!targeted) {
231            throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}");
232        }
233    }
234}