1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.generic;
20
21 import org.apache.bcel.Const;
22 import org.apache.bcel.classfile.LocalVariable;
23
24 /**
25 * Represents a local variable within a method. It contains its scope, name and type. The generated LocalVariable object
26 * can be obtained with getLocalVariable which needs the instruction list and the constant pool as parameters.
27 *
28 * @see LocalVariable
29 * @see MethodGen
30 */
31 public class LocalVariableGen implements InstructionTargeter, NamedAndTyped, Cloneable {
32
33 private int index;
34 private String name;
35 private Type type;
36 private InstructionHandle start;
37 private InstructionHandle end;
38 private int origIndex; // never changes; used to match up with LocalVariableTypeTable entries
39 private boolean liveToEnd;
40
41 /**
42 * Generate a local variable that with index 'index'. Note that double and long variables need two indexs. Index indices
43 * have to be provided by the user.
44 *
45 * @param index index of local variable.
46 * @param name its name.
47 * @param type its type.
48 * @param start from where the instruction is valid (null means from the start).
49 * @param end until where the instruction is valid (null means to the end).
50 */
51 public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end) {
52 if (index < 0 || index > Const.MAX_SHORT) {
53 throw new ClassGenException("Invalid index: " + index);
54 }
55 this.name = name;
56 this.type = type;
57 this.index = index;
58 setStart(start);
59 setEnd(end);
60 this.origIndex = index;
61 this.liveToEnd = end == null;
62 }
63
64 /**
65 * Generates a local variable that with index 'index'. Note that double and long variables need two indexs. Index
66 * indices have to be provided by the user.
67 *
68 * @param index index of local variable.
69 * @param name its name.
70 * @param type its type.
71 * @param start from where the instruction is valid (null means from the start).
72 * @param end until where the instruction is valid (null means to the end).
73 * @param origIndex index of local variable prior to any changes to index.
74 */
75 public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end,
76 final int origIndex) {
77 this(index, name, type, start, end);
78 this.origIndex = origIndex;
79 }
80
81 @Override
82 public Object clone() {
83 try {
84 return super.clone();
85 } catch (final CloneNotSupportedException e) {
86 throw new UnsupportedOperationException("Clone Not Supported", e); // never happens
87 }
88 }
89
90 /**
91 * @return true, if ih is target of this variable.
92 */
93 @Override
94 public boolean containsTarget(final InstructionHandle ih) {
95 return start == ih || end == ih;
96 }
97
98 /**
99 * 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 /**
119 * Gets the end instruction handle.
120 *
121 * @return the end instruction handle.
122 */
123 public InstructionHandle getEnd() {
124 return end;
125 }
126
127 /**
128 * Gets the index.
129 *
130 * @return the index.
131 */
132 public int getIndex() {
133 return index;
134 }
135
136 /**
137 * Gets whether the variable lives to the end.
138 *
139 * @return true if the variable lives to the end.
140 */
141 public boolean getLiveToEnd() {
142 return liveToEnd;
143 }
144
145 /**
146 * Gets LocalVariable object.
147 *
148 * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods
149 * has been called for the instruction list.
150 *
151 * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference
152 * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last
153 * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases.
154 *
155 * @param cp constant pool.
156 * @return the local variable.
157 */
158 public LocalVariable getLocalVariable(final ConstantPoolGen cp) {
159 int startPc = 0;
160 int length = 0;
161 if (start != null && end != null) {
162 startPc = start.getPosition();
163 length = end.getPosition() - startPc;
164 if (end.getNext() == null && liveToEnd) {
165 length += end.getInstruction().getLength();
166 }
167 }
168 final int nameIndex = cp.addUtf8(name);
169 final int signatureIndex = cp.addUtf8(type.getSignature());
170 return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex);
171 }
172
173 @Override
174 public String getName() {
175 return name;
176 }
177
178 /**
179 * Gets the original index.
180 *
181 * @return the original index.
182 */
183 public int getOrigIndex() {
184 return origIndex;
185 }
186
187 /**
188 * Gets the start instruction handle.
189 *
190 * @return the start instruction handle.
191 */
192 public InstructionHandle getStart() {
193 return start;
194 }
195
196 @Override
197 public Type getType() {
198 return type;
199 }
200
201 @Override
202 public int hashCode() {
203 // If the user changes the name or type, problems with the targeter hashmap will occur.
204 // Note: Index cannot be part of hash as it may be changed by the user.
205 return name.hashCode() ^ type.hashCode();
206 }
207
208 /**
209 * Sets the end instruction handle.
210 *
211 * @param end the end instruction handle.
212 */
213 public void setEnd(final InstructionHandle end) { // TODO could be package-protected?
214 BranchInstruction.notifyTarget(this.end, end, this);
215 this.end = end;
216 }
217
218 /**
219 * Sets the index.
220 *
221 * @param index the index.
222 */
223 public void setIndex(final int index) {
224 this.index = index;
225 }
226
227 /**
228 * Sets whether the variable lives to the end.
229 *
230 * @param liveToEnd true if the variable lives to the end.
231 */
232 public void setLiveToEnd(final boolean liveToEnd) {
233 this.liveToEnd = liveToEnd;
234 }
235
236 @Override
237 public void setName(final String name) {
238 this.name = name;
239 }
240
241 /**
242 * Sets the start instruction handle.
243 *
244 * @param start the start instruction handle.
245 */
246 public void setStart(final InstructionHandle start) { // TODO could be package-protected?
247 BranchInstruction.notifyTarget(this.start, start, this);
248 this.start = start;
249 }
250
251 @Override
252 public void setType(final Type type) {
253 this.type = type;
254 }
255
256 @Override
257 public String toString() {
258 return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
259 }
260
261 /**
262 * @param oldIh old target, either start or end.
263 * @param newIh new target.
264 */
265 @Override
266 public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
267 boolean targeted = false;
268 if (start == oldIh) {
269 targeted = true;
270 setStart(newIh);
271 }
272 if (end == oldIh) {
273 targeted = true;
274 setEnd(newIh);
275 }
276 if (!targeted) {
277 throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}");
278 }
279 }
280 }
281