View Javadoc
1   /*
2    * reserved comment block
3    * DO NOT REMOVE OR ALTER!
4    */
5   package com.sun.org.apache.bcel.internal.generic;
6   
7   /* ====================================================================
8    * The Apache Software License, Version 1.1
9    *
10   * Copyright (c) 2001 The Apache Software Foundation.  All rights
11   * reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions
15   * are met:
16   *
17   * 1. Redistributions of source code must retain the above copyright
18   *    notice, this list of conditions and the following disclaimer.
19   *
20   * 2. Redistributions in binary form must reproduce the above copyright
21   *    notice, this list of conditions and the following disclaimer in
22   *    the documentation and/or other materials provided with the
23   *    distribution.
24   *
25   * 3. The end-user documentation included with the redistribution,
26   *    if any, must include the following acknowledgment:
27   *       "This product includes software developed by the
28   *        Apache Software Foundation (http://www.apache.org/)."
29   *    Alternately, this acknowledgment may appear in the software itself,
30   *    if and wherever such third-party acknowledgments normally appear.
31   *
32   * 4. The names "Apache" and "Apache Software Foundation" and
33   *    "Apache BCEL" must not be used to endorse or promote products
34   *    derived from this software without prior written permission. For
35   *    written permission, please contact apache@apache.org.
36   *
37   * 5. Products derived from this software may not be called "Apache",
38   *    "Apache BCEL", nor may "Apache" appear in their name, without
39   *    prior written permission of the Apache Software Foundation.
40   *
41   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52   * SUCH DAMAGE.
53   * ====================================================================
54   *
55   * This software consists of voluntary contributions made by many
56   * individuals on behalf of the Apache Software Foundation.  For more
57   * information on the Apache Software Foundation, please see
58   * <http://www.apache.org/>.
59   */
60  
61  import com.sun.org.apache.bcel.internal.Constants;
62  import com.sun.org.apache.bcel.internal.classfile.*;
63  import java.util.Objects;
64  
65  /**
66   * This class represents a local variable within a method. It contains its
67   * scope, name and type. The generated LocalVariable object can be obtained
68   * with getLocalVariable which needs the instruction list and the constant
69   * pool as parameters.
70   *
71   * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
72   * @see     LocalVariable
73   * @see     MethodGen
74   */
75  public class LocalVariableGen
76    implements InstructionTargeter, NamedAndTyped, Cloneable,
77               java.io.Serializable
78  {
79    private final int   index;
80    private String      name;
81    private Type        type;
82    private InstructionHandle start, end;
83  
84    /**
85     * Generate a local variable that with index `index'. Note that double and long
86     * variables need two indexs. Index indices have to be provided by the user.
87     *
88     * @param index index of local variable
89     * @param name its name
90     * @param type its type
91     * @param start from where the instruction is valid (null means from the start)
92     * @param end until where the instruction is valid (null means to the end)
93     */
94    public LocalVariableGen(int index, String name, Type type,
95                            InstructionHandle start, InstructionHandle end) {
96      if((index < 0) || (index > Constants.MAX_SHORT))
97        throw new ClassGenException("Invalid index index: " + index);
98  
99      this.name  = name;
100     this.type  = type;
101     this.index  = index;
102     setStart(start);
103     setEnd(end);
104   }
105 
106   /**
107    * Get LocalVariable object.
108    *
109    * This relies on that the instruction list has already been dumped to byte code or
110    * or that the `setPositions' methods has been called for the instruction list.
111    *
112    * Note that for local variables whose scope end at the last
113    * instruction of the method's code, the JVM specification is ambiguous:
114    * both a start_pc+length ending at the last instruction and
115    * start_pc+length ending at first index beyond the end of the code are
116    * valid.
117    *
118    * @param il instruction list (byte code) which this variable belongs to
119    * @param cp constant pool
120    */
121   public LocalVariable getLocalVariable(ConstantPoolGen cp) {
122     int start_pc        = start.getPosition();
123     int length          = end.getPosition() - start_pc;
124 
125     if(length > 0)
126       length += end.getInstruction().getLength();
127 
128     int name_index      = cp.addUtf8(name);
129     int signature_index = cp.addUtf8(type.getSignature());
130 
131     return new LocalVariable(start_pc, length, name_index,
132                              signature_index, index, cp.getConstantPool());
133   }
134 
135   public int         getIndex()                  { return index; }
136   @Override
137   public void        setName(String name)        { this.name = name; }
138   @Override
139   public String      getName()                   { return name; }
140   @Override
141   public void        setType(Type type)          { this.type = type; }
142   @Override
143   public Type        getType()                   { return type; }
144 
145   public InstructionHandle getStart()                  { return start; }
146   public InstructionHandle getEnd()                    { return end; }
147 
148   /**
149    * Remove this from any known HashSet in which it might be registered.
150    */
151   void notifyTargetChanging() {
152     // hashCode depends on 'index', 'start', and 'end'.
153     // Therefore before changing any of these values we
154     // need to unregister 'this' from any HashSet where
155     // this is registered, and then we need to add it
156     // back...
157 
158     // Unregister 'this' from the HashSet held by 'start'.
159     BranchInstruction.notifyTargetChanging(this.start, this);
160     if (this.end != this.start) {
161         // Since hashCode() is going to change we need to unregister
162         // 'this' both form 'start' and 'end'.
163         // Unregister 'this' from the HashSet held by 'end'.
164         BranchInstruction.notifyTargetChanging(this.end, this);
165     }
166   }
167 
168   /**
169    * Add back 'this' in all HashSet in which it should be registered.
170    **/
171   void notifyTargetChanged() {
172     // hashCode depends on 'index', 'start', and 'end'.
173     // Therefore before changing any of these values we
174     // need to unregister 'this' from any HashSet where
175     // this is registered, and then we need to add it
176     // back...
177 
178     // Register 'this' in the HashSet held by start.
179     BranchInstruction.notifyTargetChanged(this.start, this);
180     if (this.end != this.start) {
181         // Since hashCode() has changed we need to register
182         // 'this' again in 'end'.
183         // Add back 'this' in the HashSet held by 'end'.
184         BranchInstruction.notifyTargetChanged(this.end, this);
185     }
186   }
187 
188   public final void setStart(InstructionHandle start) {
189 
190     // Call notifyTargetChanging *before* modifying this,
191     // as the code triggered by notifyTargetChanging
192     // depends on this pointing to the 'old' start.
193     notifyTargetChanging();
194 
195     this.start = start;
196 
197     // call notifyTargetChanged *after* modifying this,
198     // as the code triggered by notifyTargetChanged
199     // depends on this pointing to the 'new' start.
200     notifyTargetChanged();
201   }
202 
203   public final void setEnd(InstructionHandle end) {
204     // call notifyTargetChanging *before* modifying this,
205     // as the code triggered by notifyTargetChanging
206     // depends on this pointing to the 'old' end.
207     // Unregister 'this' from the HashSet held by the 'old' end.
208     notifyTargetChanging();
209 
210     this.end = end;
211 
212     // call notifyTargetChanged *after* modifying this,
213     // as the code triggered by notifyTargetChanged
214     // depends on this pointing to the 'new' end.
215     // Register 'this' in the HashSet held by the 'new' end.
216     notifyTargetChanged();
217 
218   }
219 
220   /**
221    * @param old_ih old target, either start or end
222    * @param new_ih new target
223    */
224   @Override
225   public void updateTarget(InstructionHandle old_ih, InstructionHandle new_ih) {
226     boolean targeted = false;
227 
228     if(start == old_ih) {
229       targeted = true;
230       setStart(new_ih);
231     }
232 
233     if(end == old_ih) {
234       targeted = true;
235       setEnd(new_ih);
236     }
237 
238     if(!targeted)
239       throw new ClassGenException("Not targeting " + old_ih + ", but {" + start + ", " +
240                                   end + "}");
241   }
242 
243   /**
244    * @return true, if ih is target of this variable
245    */
246   @Override
247   public boolean containsTarget(InstructionHandle ih) {
248     return (start == ih) || (end == ih);
249   }
250 
251   /**
252    * We consider two local variables to be equal, if they use the same index and
253    * are valid in the same range.
254    */
255   @Override
256   public boolean equals(Object o) {
257     if (o==this)
258       return true;
259 
260     if(!(o instanceof LocalVariableGen))
261       return false;
262 
263     LocalVariableGen l = (LocalVariableGen)o;
264     return (l.index == index) && (l.start == start) && (l.end == end);
265   }
266 
267   @Override
268   public int hashCode() {
269     int hash = 7;
270     hash = 59 * hash + this.index;
271     hash = 59 * hash + Objects.hashCode(this.start);
272     hash = 59 * hash + Objects.hashCode(this.end);
273     return hash;
274   }
275 
276   @Override
277   public String toString() {
278     return "LocalVariableGen(" + name +  ", " + type +  ", " + start + ", " + end + ")";
279   }
280 
281   @Override
282   public Object clone() {
283     try {
284       return super.clone();
285     } catch(CloneNotSupportedException e) {
286       System.err.println(e);
287       return null;
288     }
289   }
290 }