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