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