View Javadoc
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