1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18 package org.apache.bcel.generic;
19 
20 import org.apache.bcel.Const;
21 import org.apache.bcel.classfile.LocalVariable;
22 
23 /**
24  * This class represents a local variable within a method. It contains its
25  * scope, name and type. The generated LocalVariable object can be obtained
26  * with getLocalVariable which needs the instruction list and the constant
27  * pool as parameters.
28  *
29  * @version $Id$
30  * @see     LocalVariable
31  * @see     MethodGen
32  */
33 public class LocalVariableGen implements InstructionTargeter, NamedAndTyped, Cloneable {
34 
35     private int index;
36     private String name;
37     private Type type;
38     private InstructionHandle start;
39     private InstructionHandle end;
40     private int orig_index; // never changes; used to match up with LocalVariableTypeTable entries
41     private boolean live_to_end;
42 
43 
44     /**
45      * Generate a local variable that with index `index'. Note that double and long
46      * variables need two indexs. Index indices have to be provided by the user.
47      *
48      * @param index index of local variable
49      * @param name its name
50      * @param type its type
51      * @param start from where the instruction is valid (null means from the start)
52      * @param end until where the instruction is valid (null means to the end)
53      */
LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end)54     public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start,
55             final InstructionHandle end) {
56         if ((index < 0) || (index > Const.MAX_SHORT)) {
57             throw new ClassGenException("Invalid index index: " + index);
58         }
59         this.name = name;
60         this.type = type;
61         this.index = index;
62         setStart(start);
63         setEnd(end);
64         this.orig_index = index;
65         this.live_to_end = end == null;
66     }
67 
68 
69     /**
70      * Generate a local variable that with index `index'. Note that double and long
71      * variables need two indexs. Index indices have to be provided by the user.
72      *
73      * @param index index of local variable
74      * @param name its name
75      * @param type its type
76      * @param start from where the instruction is valid (null means from the start)
77      * @param end until where the instruction is valid (null means to the end)
78      * @param orig_index index of local variable prior to any changes to index
79      */
LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end, final int orig_index)80     public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start,
81             final InstructionHandle end, final int orig_index) {
82         this(index, name, type, start, end);
83         this.orig_index = orig_index;
84     }
85 
86 
87     /**
88      * Get LocalVariable object.
89      *
90      * This relies on that the instruction list has already been dumped to byte code or
91      * or that the `setPositions' methods has been called for the instruction list.
92      *
93      * Note that due to the conversion from byte code offset to InstructionHandle,
94      * it is impossible to tell the difference between a live range that ends BEFORE
95      * the last insturction of the method or a live range that ends AFTER the last
96      * instruction of the method.  Hence the live_to_end flag to differentiate
97      * between these two cases.
98      *
99      * @param cp constant pool
100      */
getLocalVariable( final ConstantPoolGen cp )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 
setIndex( final int index )118     public void setIndex( final int index ) {
119         this.index = index;
120     }
121 
122 
getIndex()123     public int getIndex() {
124         return index;
125     }
126 
127 
getOrigIndex()128     public int getOrigIndex() {
129         return orig_index;
130     }
131 
132 
setLiveToEnd( final boolean live_to_end)133     public void setLiveToEnd( final boolean live_to_end) {
134         this.live_to_end = live_to_end;
135     }
136 
137 
getLiveToEnd()138     public boolean getLiveToEnd() {
139         return live_to_end;
140     }
141 
142 
143     @Override
setName( final String name )144     public void setName( final String name ) {
145         this.name = name;
146     }
147 
148 
149     @Override
getName()150     public String getName() {
151         return name;
152     }
153 
154 
155     @Override
setType( final Type type )156     public void setType( final Type type ) {
157         this.type = type;
158     }
159 
160 
161     @Override
getType()162     public Type getType() {
163         return type;
164     }
165 
166 
getStart()167     public InstructionHandle getStart() {
168         return start;
169     }
170 
171 
getEnd()172     public InstructionHandle getEnd() {
173         return end;
174     }
175 
176 
setStart( final InstructionHandle start )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 
setEnd( final InstructionHandle end )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
updateTarget( final InstructionHandle old_ih, final InstructionHandle new_ih )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      */
dispose()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
containsTarget( final InstructionHandle ih )222     public boolean containsTarget( final InstructionHandle ih ) {
223         return (start == ih) || (end == ih);
224     }
225 
226 
227     @Override
hashCode()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
equals( final Object o )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
toString()250     public String toString() {
251         return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
252     }
253 
254 
255     @Override
clone()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 }
264