View Javadoc

1   /*
2    * Copyright  2000-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License"); 
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License. 
15   *
16   */ 
17  package org.apache.bcel.verifier.structurals;
18  
19  
20  import org.apache.bcel.generic.ReferenceType;
21  import org.apache.bcel.generic.Type;
22  import org.apache.bcel.verifier.exc.AssertionViolatedException;
23  import org.apache.bcel.verifier.exc.StructuralCodeConstraintException;
24  
25  /***
26   * This class implements an array of local variables used for symbolic JVM
27   * simulation.
28   *
29   * @version $Id: LocalVariables.java 386056 2006-03-15 11:31:56Z tcurdt $
30   * @author Enver Haase
31   */
32  public class LocalVariables{
33  	/*** The Type[] containing the local variable slots. */
34  	private Type[] locals;
35  
36  	/***
37  	 * Creates a new LocalVariables object.
38  	 */
39  	public LocalVariables(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  	protected Object clone(){
52  		LocalVariables lvs = new LocalVariables(locals.length);
53  		for (int i=0; i<locals.length; i++){
54  			lvs.locals[i] = this.locals[i];
55  		}
56  		return lvs;
57  	}
58  
59  	/***
60  	 * Returns the type of the local variable slot i.
61  	 */
62  	public Type get(int i){
63  		return locals[i];
64  	}
65  
66  	/***
67  	 * Returns a (correctly typed) clone of this object.
68  	 * This is equivalent to ((LocalVariables) this.clone()).
69  	 */
70  	public LocalVariables getClone(){
71  		return (LocalVariables) this.clone();
72  	}
73  
74  	/***
75  	 * Returns the number of local variable slots this
76  	 * LocalVariables instance has.
77  	 */
78  	public int maxLocals(){
79  		return locals.length;
80  	}
81  
82  	/***
83  	 * Sets a new Type for the given local variable slot.
84  	 */
85  	public void set(int i, Type type){
86  		if (type == Type.BYTE || type == Type.SHORT || type == Type.BOOLEAN || type == Type.CHAR){
87  			throw new AssertionViolatedException("LocalVariables do not know about '"+type+"'. Use Type.INT instead.");
88  		}
89  		locals[i] = type;
90  	}
91  
92  	/*** @return a hash code value for the object.
93       */
94  	public int hashCode() { return locals.length; }
95  
96  	/*
97  	 * Fulfills the general contract of Object.equals().
98  	 */
99  	public boolean equals(Object o){
100 		if (!(o instanceof LocalVariables)) {
101             return false;
102         }
103 		LocalVariables lv = (LocalVariables) o;
104 		if (this.locals.length != lv.locals.length) {
105             return false;
106         }
107 		for (int i=0; i<this.locals.length; i++){
108 			if (!this.locals[i].equals(lv.locals[i])){
109 				//System.out.println(this.locals[i]+" is not "+lv.locals[i]);
110 				return false;
111 			}
112 		}
113 		return true;
114 	}
115 	
116 	/***
117 	 * Merges two local variables sets as described in the Java Virtual Machine Specification,
118 	 * Second Edition, section 4.9.2, page 146.
119 	 */
120 	public void merge(LocalVariables lv){
121 
122 		if (this.locals.length != lv.locals.length){
123 			throw new AssertionViolatedException("Merging LocalVariables of different size?!? From different methods or what?!?");
124 		}
125 
126 		for (int i=0; i<locals.length; i++){
127 			merge(lv, i);
128 		}
129 	}
130 	
131 	/***
132 	 * Merges a single local variable.
133 	 *
134 	 * @see #merge(LocalVariables)
135 	 */
136 	private void merge(LocalVariables lv, int i){
137 	    try {
138 		
139 		// We won't accept an unitialized object if we know it was initialized;
140 		// compare vmspec2, 4.9.4, last paragraph.
141 		if ( (!(locals[i] instanceof UninitializedObjectType)) && (lv.locals[i] instanceof UninitializedObjectType) ){
142 			throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected.");
143 		}
144 		// Even harder, what about _different_ uninitialized object types?!
145 		if ( (!(locals[i].equals(lv.locals[i]))) && (locals[i] instanceof UninitializedObjectType) && (lv.locals[i] instanceof UninitializedObjectType) ){
146 			throw new StructuralCodeConstraintException("Backwards branch with an uninitialized object in the local variables detected.");
147 		}
148 		// If we just didn't know that it was initialized, we have now learned.
149 		if (locals[i] instanceof UninitializedObjectType){
150 			if (! (lv.locals[i] instanceof UninitializedObjectType)){
151 				locals[i] = ((UninitializedObjectType) locals[i]).getInitialized();
152 			}
153 		}
154 		if ((locals[i] instanceof ReferenceType) && (lv.locals[i] instanceof ReferenceType)){
155 			if (! locals[i].equals(lv.locals[i])){ // needed in case of two UninitializedObjectType instances
156 				Type sup = ((ReferenceType) locals[i]).getFirstCommonSuperclass((ReferenceType) (lv.locals[i]));
157 
158 				if (sup != null){
159 					locals[i] = sup;
160 				}
161 				else{
162 					// We should have checked this in Pass2!
163 					throw new AssertionViolatedException("Could not load all the super classes of '"+locals[i]+"' and '"+lv.locals[i]+"'.");
164 				}
165 			}
166 		}
167 		else{
168 			if (! (locals[i].equals(lv.locals[i])) ){
169 /*TODO
170 				if ((locals[i] instanceof org.apache.bcel.generic.ReturnaddressType) && (lv.locals[i] instanceof org.apache.bcel.generic.ReturnaddressType)){
171 					//System.err.println("merging "+locals[i]+" and "+lv.locals[i]);
172 					throw new AssertionViolatedException("Merging different ReturnAddresses: '"+locals[i]+"' and '"+lv.locals[i]+"'.");
173 				}
174 */
175 				locals[i] = Type.UNKNOWN;
176 			}
177 		}
178 	    } catch (ClassNotFoundException e) {
179 		// FIXME: maybe not the best way to handle this
180 		throw new AssertionViolatedException("Missing class: " + e.toString());
181 	    }
182 	}
183 
184 	/***
185 	 * Returns a String representation of this object.
186 	 */
187 	public String toString(){
188 		StringBuffer sb = new StringBuffer();
189 		for (int i=0; i<locals.length; i++){
190 			sb.append(Integer.toString(i));
191 			sb.append(": ");
192 			sb.append(locals[i]);
193 			sb.append("\n");
194 		}
195 		return sb.toString();
196 	}
197 
198 	/***
199 	 * Replaces all occurences of u in this local variables set
200 	 * with an "initialized" ObjectType.
201 	 */
202 	public void initializeObject(UninitializedObjectType u){
203 		for (int i=0; i<locals.length; i++){
204 			if (locals[i] == u){
205 				locals[i] = u.getInitialized();
206 			}
207 		}
208 	}
209 }