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   */
18  package org.apache.bcel.verifier.structurals;
19  
20  
21  import org.apache.bcel.generic.ReferenceType;
22  import org.apache.bcel.generic.Type;
23  import org.apache.bcel.verifier.exc.AssertionViolatedException;
24  import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
25  
26  /**
27   * This class implements an array of local variables used for symbolic JVM
28   * simulation.
29   */
30  public class LocalVariables implements Cloneable {
31  
32      /** The Type[] containing the local variable slots. */
33      private final Type[] locals;
34  
35      /**
36       * Creates a new LocalVariables object.
37       *
38       * @param localVariableCount local variable count.
39       */
40      public LocalVariables(final int localVariableCount) {
41          locals = new Type[localVariableCount];
42          for (int i=0; i<localVariableCount; i++) {
43              locals[i] = Type.UNKNOWN;
44          }
45      }
46  
47      /**
48       * Returns a deep copy of this object; i.e. the clone
49       * operates on a new local variable array.
50       * However, the Type objects in the array are shared.
51       */
52      @Override
53      public Object clone() {
54          final LocalVariablesturals/LocalVariables.html#LocalVariables">LocalVariables lvs = new LocalVariables(locals.length);
55          for (int i=0; i<locals.length; i++) {
56              lvs.locals[i] = this.locals[i];
57          }
58          return lvs;
59      }
60  
61      /**
62       * Returns the type of the local variable slot index.
63       *
64       * @param slotIndex Slot to look up.
65       * @return the type of the local variable slot index.
66       */
67      public Type get(final int slotIndex) {
68          return locals[slotIndex];
69      }
70  
71      /**
72       * Returns a (correctly typed) clone of this object.
73       * This is equivalent to ((LocalVariables) this.clone()).
74       *
75       * @return a (correctly typed) clone of this object.
76       */
77      public LocalVariables getClone() {
78          return (LocalVariables) this.clone();
79      }
80  
81      /**
82       * Returns the number of local variable slots.
83       *
84       * @return the number of local variable slots.
85       */
86      public int maxLocals() {
87          return locals.length;
88      }
89  
90      /**
91       * Sets a new Type for the given local variable slot.
92       *
93       * @param slotIndex Target slot index.
94       * @param type Type to save at the given slot index.
95       */
96      public void set(final int slotIndex, final Type type) { // TODO could be package-protected?
97          if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR) {
98              throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead.");
99          }
100         locals[slotIndex] = type;
101     }
102 
103     /** @return a hash code value for the object.
104      */
105     @Override
106     public int hashCode() { return locals.length; }
107 
108     /*
109      * Fulfills the general contract of Object.equals().
110      */
111     @Override
112     public boolean equals(final Object o) {
113         if (!(o instanceof LocalVariables)) {
114             return false;
115         }
116         final LocalVariables./../../../org/apache/bcel/verifier/structurals/LocalVariables.html#LocalVariables">LocalVariables lv = (LocalVariables) o;
117         if (this.locals.length != lv.locals.length) {
118             return false;
119         }
120         for (int i=0; i<this.locals.length; i++) {
121             if (!this.locals[i].equals(lv.locals[i])) {
122                 //System.out.println(this.locals[i]+" is not "+lv.locals[i]);
123                 return false;
124             }
125         }
126         return true;
127     }
128 
129     /**
130      * Merges two local variables sets as described in the Java Virtual Machine Specification,
131      * Second Edition, section 4.9.2, page 146.
132      *
133      * @param localVariable other local variable.
134      */
135     public void merge(final LocalVariables localVariable) {
136 
137         if (this.locals.length != localVariable.locals.length) {
138             throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?");
139         }
140 
141         for (int i=0; i<locals.length; i++) {
142             merge(localVariable, i);
143         }
144     }
145 
146     /**
147      * Merges a single local variable.
148      *
149      * @see #merge(LocalVariables)
150      */
151     private void merge(final LocalVariables lv, final int i) {
152         try {
153 
154         // We won't accept an unitialized object if we know it was initialized;
155         // compare vmspec2, 4.9.4, last paragraph.
156         if ( (!(locals[i] instanceof UninitializedObjectTypeturals/UninitializedObjectType.html#UninitializedObjectType">UninitializedObjectType)) && (lv.locals[i] instanceof UninitializedObjectType) ) {
157             throw new StructuralCodeConstraintException(
158                 "Backwards branch with an uninitialized object in the local variables detected.");
159         }
160         // Even harder, what about _different_ uninitialized object types?!
161         if ( (!(locals[i].equals(lv.locals[i]))) && (locals[i] instanceof UninitializedObjectType) &&
162                 (lv.locals[i] instanceof UninitializedObjectType) ) {
163             throw new StructuralCodeConstraintException(
164                 "Backwards branch with an uninitialized object in the local variables detected.");
165         }
166         // If we just didn't know that it was initialized, we have now learned.
167         if (locals[i] instanceof UninitializedObjectType) {
168             if (! (lv.locals[i] instanceof UninitializedObjectType)) {
169                 locals[i] = ((UninitializedObjectType) locals[i]).getInitialized();
170             }
171         }
172         if ((locals[i] instanceof ReferenceTypeeric/ReferenceType.html#ReferenceType">ReferenceType) && (lv.locals[i] instanceof ReferenceType)) {
173             if (! locals[i].equals(lv.locals[i])) { // needed in case of two UninitializedObjectType instances
174                 final Type sup = ((ReferenceTyperic/ReferenceType.html#ReferenceType">ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) (lv.locals[i]));
175 
176                 if (sup != null) {
177                     locals[i] = sup;
178                 }
179                 else{
180                     // We should have checked this in Pass2!
181                     throw new AssertionViolatedException(
182                         "Could not load all the super classes of '"+locals[i]+"' and '"+lv.locals[i]+"'.");
183                 }
184             }
185         }
186         else{
187             if (! (locals[i].equals(lv.locals[i])) ) {
188 /*TODO
189                 if ((locals[i] instanceof org.apache.bcel.generic.ReturnaddressType) &&
190                     (lv.locals[i] instanceof org.apache.bcel.generic.ReturnaddressType)) {
191                     //System.err.println("merging "+locals[i]+" and "+lv.locals[i]);
192                     throw new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'.");
193                 }
194 */
195                 locals[i] = Type.UNKNOWN;
196             }
197         }
198         } catch (final ClassNotFoundException e) {
199         // FIXME: maybe not the best way to handle this
200         throw new AssertionViolatedException("Missing class: " + e, e);
201         }
202     }
203 
204     /**
205      * Returns a String representation of this object.
206      */
207     @Override
208     public String toString() {
209         final StringBuilder sb = new StringBuilder();
210         for (int i=0; i<locals.length; i++) {
211             sb.append(Integer.toString(i));
212             sb.append(": ");
213             sb.append(locals[i]);
214             sb.append("\n");
215         }
216         return sb.toString();
217     }
218 
219     /**
220      * Replaces all occurrences of {@code uninitializedObjectType} in this local variables set
221      * with an "initialized" ObjectType.
222      *
223      * @param uninitializedObjectType the object to match.
224      */
225     public void initializeObject(final UninitializedObjectType uninitializedObjectType) {
226         for (int i=0; i<locals.length; i++) {
227             if (locals[i] == uninitializedObjectType) {
228                 locals[i] = uninitializedObjectType.getInitialized();
229             }
230         }
231     }
232 }