View Javadoc
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  package org.apache.bcel.generic;
18  
19  import org.apache.bcel.Const;
20  import org.apache.bcel.classfile.LocalVariable;
21  
22  /**
23   * Represents a local variable within a method. It contains its scope, name and type. The generated LocalVariable object
24   * can be obtained with getLocalVariable which needs the instruction list and the constant pool as parameters.
25   *
26   * @see LocalVariable
27   * @see MethodGen
28   */
29  public class LocalVariableGen implements InstructionTargeter, NamedAndTyped, Cloneable {
30  
31      private int index;
32      private String name;
33      private Type type;
34      private InstructionHandle start;
35      private InstructionHandle end;
36      private int origIndex; // never changes; used to match up with LocalVariableTypeTable entries
37      private boolean liveToEnd;
38  
39      /**
40       * Generate a local variable that with index 'index'. Note that double and long variables need two indexs. Index indices
41       * have to be provided by the user.
42       *
43       * @param index index of local variable
44       * @param name its name
45       * @param type its type
46       * @param start from where the instruction is valid (null means from the start)
47       * @param end until where the instruction is valid (null means to the end)
48       */
49      public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end) {
50          if (index < 0 || index > Const.MAX_SHORT) {
51              throw new ClassGenException("Invalid index: " + index);
52          }
53          this.name = name;
54          this.type = type;
55          this.index = index;
56          setStart(start);
57          setEnd(end);
58          this.origIndex = index;
59          this.liveToEnd = end == null;
60      }
61  
62      /**
63       * Generates a local variable that with index 'index'. Note that double and long variables need two indexs. Index
64       * indices have to be provided by the user.
65       *
66       * @param index index of local variable
67       * @param name its name
68       * @param type its type
69       * @param start from where the instruction is valid (null means from the start)
70       * @param end until where the instruction is valid (null means to the end)
71       * @param origIndex index of local variable prior to any changes to index
72       */
73      public LocalVariableGen(final int index, final String name, final Type type, final InstructionHandle start, final InstructionHandle end,
74          final int origIndex) {
75          this(index, name, type, start, end);
76          this.origIndex = origIndex;
77      }
78  
79      @Override
80      public Object clone() {
81          try {
82              return super.clone();
83          } catch (final CloneNotSupportedException e) {
84              throw new UnsupportedOperationException("Clone Not Supported", e); // never happens
85          }
86      }
87  
88      /**
89       * @return true, if ih is target of this variable
90       */
91      @Override
92      public boolean containsTarget(final InstructionHandle ih) {
93          return start == ih || end == ih;
94      }
95  
96      /**
97       * Clear the references from and to this variable when it's removed.
98       */
99      void dispose() {
100         setStart(null);
101         setEnd(null);
102     }
103 
104     /**
105      * We consider to local variables to be equal, if the use the same index and are valid in the same range.
106      */
107     @Override
108     public boolean equals(final Object o) {
109         if (!(o instanceof LocalVariableGen)) {
110             return false;
111         }
112         final LocalVariableGen l = (LocalVariableGen) o;
113         return l.index == index && l.start == start && l.end == end;
114     }
115 
116     public InstructionHandle getEnd() {
117         return end;
118     }
119 
120     public int getIndex() {
121         return index;
122     }
123 
124     public boolean getLiveToEnd() {
125         return liveToEnd;
126     }
127 
128     /**
129      * Gets LocalVariable object.
130      *
131      * This relies on that the instruction list has already been dumped to byte code or that the 'setPositions' methods
132      * has been called for the instruction list.
133      *
134      * Note that due to the conversion from byte code offset to InstructionHandle, it is impossible to tell the difference
135      * between a live range that ends BEFORE the last insturction of the method or a live range that ends AFTER the last
136      * instruction of the method. Hence the liveToEnd flag to differentiate between these two cases.
137      *
138      * @param cp constant pool
139      */
140     public LocalVariable getLocalVariable(final ConstantPoolGen cp) {
141         int startPc = 0;
142         int length = 0;
143         if (start != null && end != null) {
144             startPc = start.getPosition();
145             length = end.getPosition() - startPc;
146             if (end.getNext() == null && liveToEnd) {
147                 length += end.getInstruction().getLength();
148             }
149         }
150         final int nameIndex = cp.addUtf8(name);
151         final int signatureIndex = cp.addUtf8(type.getSignature());
152         return new LocalVariable(startPc, length, nameIndex, signatureIndex, index, cp.getConstantPool(), origIndex);
153     }
154 
155     @Override
156     public String getName() {
157         return name;
158     }
159 
160     public int getOrigIndex() {
161         return origIndex;
162     }
163 
164     public InstructionHandle getStart() {
165         return start;
166     }
167 
168     @Override
169     public Type getType() {
170         return type;
171     }
172 
173     @Override
174     public int hashCode() {
175         // If the user changes the name or type, problems with the targeter hashmap will occur.
176         // Note: index cannot be part of hash as it may be changed by the user.
177         return name.hashCode() ^ type.hashCode();
178     }
179 
180     public void setEnd(final InstructionHandle end) { // TODO could be package-protected?
181         BranchInstruction.notifyTarget(this.end, end, this);
182         this.end = end;
183     }
184 
185     public void setIndex(final int index) {
186         this.index = index;
187     }
188 
189     public void setLiveToEnd(final boolean liveToEnd) {
190         this.liveToEnd = liveToEnd;
191     }
192 
193     @Override
194     public void setName(final String name) {
195         this.name = name;
196     }
197 
198     public void setStart(final InstructionHandle start) { // TODO could be package-protected?
199         BranchInstruction.notifyTarget(this.start, start, this);
200         this.start = start;
201     }
202 
203     @Override
204     public void setType(final Type type) {
205         this.type = type;
206     }
207 
208     @Override
209     public String toString() {
210         return "LocalVariableGen(" + name + ", " + type + ", " + start + ", " + end + ")";
211     }
212 
213     /**
214      * @param oldIh old target, either start or end
215      * @param newIh new target
216      */
217     @Override
218     public void updateTarget(final InstructionHandle oldIh, final InstructionHandle newIh) {
219         boolean targeted = false;
220         if (start == oldIh) {
221             targeted = true;
222             setStart(newIh);
223         }
224         if (end == oldIh) {
225             targeted = true;
226             setEnd(newIh);
227         }
228         if (!targeted) {
229             throw new ClassGenException("Not targeting " + oldIh + ", but {" + start + ", " + end + "}");
230         }
231     }
232 }