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$
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      */
LocalVariables(final int maxLocals)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
clone()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      */
get(final int i)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      */
getClone()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      */
maxLocals()79     public int maxLocals() {
80         return locals.length;
81     }
82 
83     /**
84      * Sets a new Type for the given local variable slot.
85      */
set(final int i, final Type type)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
hashCode()96     public int hashCode() { return locals.length; }
97 
98     /*
99      * Fulfills the general contract of Object.equals().
100      */
101     @Override
equals(final Object o)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      */
merge(final LocalVariables lv)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      */
merge(final LocalVariables lv, final int i)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
toString()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      */
initializeObject(final UninitializedObjectType u)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 }
219