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