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