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