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