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 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 }