1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard.evaluation; 22 23import proguard.evaluation.value.*; 24 25/** 26 * This class represents a local variable frame that contains <code>Value</code> 27 * objects. Values are generalizations of all values that have been stored in 28 * the respective variables. 29 * 30 * @author Eric Lafortune 31 */ 32public class Variables 33{ 34 private static final TopValue TOP_VALUE = new TopValue(); 35 36 37 protected Value[] values; 38 protected int size; 39 40 41 /** 42 * Creates a new Variables object with a given maximum number of variables. 43 */ 44 public Variables(int size) 45 { 46 this.values = new Value[size]; 47 this.size = size; 48 } 49 50 51 /** 52 * Creates a Variables object that is a copy of the given Variables object. 53 */ 54 public Variables(Variables variables) 55 { 56 // Create the values array. 57 this(variables.size); 58 59 // Copy the values. 60 initialize(variables); 61 } 62 63 64 /** 65 * Resets this Variables object, so that it can be reused. 66 */ 67 public void reset(int size) 68 { 69 // Is the values array large enough? 70 if (size > values.length) 71 { 72 // Create a new one. 73 values = new Value[size]; 74 } 75 else 76 { 77 // Clear the variables. 78 for (int index = 0; index < values.length; index++) 79 { 80 values[index] = null; 81 } 82 } 83 84 this.size = size; 85 } 86 87 88 /** 89 * Initializes the values of this Variables object with the values of the 90 * given Variables object. The other object may have fewer values, in which 91 * case the remaining values are left unchanged. 92 */ 93 public void initialize(Variables other) 94 { 95 if (this.size < other.size) 96 { 97 throw new IllegalArgumentException("Variable frame is too small ["+this.size+"] compared to other frame ["+other.size+"]"); 98 } 99 100 // Copy the values. 101 System.arraycopy(other.values, 0, this.values, 0, other.size); 102 } 103 104 105 /** 106 * Generalizes the values of this Variables object with the values of the 107 * given Variables object. 108 * @param clearConflictingOtherVariables specifies whether the other 109 * variables should be cleared too, 110 * in case of conflicts. 111 * @return whether the generalization has made any difference. 112 */ 113 public boolean generalize(Variables other, 114 boolean clearConflictingOtherVariables) 115 { 116 if (this.size != other.size) 117 { 118 throw new IllegalArgumentException("Variable frames have different sizes ["+this.size+"] and ["+other.size+"]"); 119 } 120 121 boolean changed = false; 122 123 for (int index = 0; index < size; index++) 124 { 125 Value thisValue = this.values[index]; 126 Value otherValue = other.values[index]; 127 128 // Occasionally, two values of different types might be present 129 // in the same variable in a variable frame (corresponding to 130 // two local variables that share the same index), at some point 131 // outside of their scopes. Don't generalize the variable then, 132 // but let it clear instead. 133 if (thisValue != null && 134 otherValue != null && 135 thisValue.computationalType() == otherValue.computationalType()) 136 { 137 Value newValue = thisValue.generalize(otherValue); 138 139 changed = changed || !thisValue.equals(newValue); 140 141 this.values[index] = newValue; 142 } 143 else 144 { 145 changed = changed || thisValue != null; 146 147 this.values[index] = null; 148 149 if (clearConflictingOtherVariables) 150 { 151 other.values[index] = null; 152 } 153 } 154 } 155 156 return changed; 157 } 158 159 160 /** 161 * Returns the number of variables. 162 */ 163 public int size() 164 { 165 return size; 166 } 167 168 169 /** 170 * Gets the Value of the variable with the given index, without disturbing it. 171 */ 172 public Value getValue(int index) 173 { 174 if (index < 0 || 175 index >= size) 176 { 177 throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); 178 } 179 180 return values[index]; 181 } 182 183 184 /** 185 * Stores the given Value at the given variable index. 186 */ 187 public void store(int index, Value value) 188 { 189 if (index < 0 || 190 index >= size) 191 { 192 throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); 193 } 194 195 // Store the value. 196 values[index] = value; 197 198 // Account for the extra space required by Category 2 values. 199 if (value.isCategory2()) 200 { 201 values[index + 1] = TOP_VALUE; 202 } 203 } 204 205 206 /** 207 * Loads the Value from the variable with the given index. 208 */ 209 public Value load(int index) 210 { 211 if (index < 0 || 212 index >= size) 213 { 214 throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); 215 } 216 217 return values[index]; 218 } 219 220 221 // Load methods that provide convenient casts to the expected value types. 222 223 /** 224 * Loads the IntegerValue from the variable with the given index. 225 */ 226 public IntegerValue iload(int index) 227 { 228 return load(index).integerValue(); 229 } 230 231 232 /** 233 * Loads the LongValue from the variable with the given index. 234 */ 235 public LongValue lload(int index) 236 { 237 return load(index).longValue(); 238 } 239 240 241 /** 242 * Loads the FloatValue from the variable with the given index. 243 */ 244 public FloatValue fload(int index) 245 { 246 return load(index).floatValue(); 247 } 248 249 250 /** 251 * Loads the DoubleValue from the variable with the given index. 252 */ 253 public DoubleValue dload(int index) 254 { 255 return load(index).doubleValue(); 256 } 257 258 259 /** 260 * Loads the ReferenceValue from the variable with the given index. 261 */ 262 public ReferenceValue aload(int index) 263 { 264 return load(index).referenceValue(); 265 } 266 267 268 /** 269 * Loads the InstructionOffsetValue from the variable with the given index. 270 */ 271 public InstructionOffsetValue oload(int index) 272 { 273 return load(index).instructionOffsetValue(); 274 } 275 276 277 // Implementations for Object. 278 279 public boolean equals(Object object) 280 { 281 if (object == null || 282 this.getClass() != object.getClass()) 283 { 284 return false; 285 } 286 287 Variables other = (Variables)object; 288 289 if (this.size != other.size) 290 { 291 return false; 292 } 293 294 for (int index = 0; index < size; index++) 295 { 296 Value thisValue = this.values[index]; 297 Value otherValue = other.values[index]; 298 299 // Occasionally, two values of different types might be 300 // present in the same variable in a variable frame 301 // (corresponding to two local variables that share the 302 // same index), at some point outside of their scopes. 303 // We'll ignore these. 304 if (thisValue != null && 305 otherValue != null && 306 thisValue.computationalType() == otherValue.computationalType() && 307 !thisValue.equals(otherValue)) 308 { 309 return false; 310 } 311 } 312 313 return true; 314 } 315 316 317 public int hashCode() 318 { 319 int hashCode = size; 320 321 for (int index = 0; index < size; index++) 322 { 323 Value value = values[index]; 324 if (value != null) 325 { 326 hashCode ^= value.hashCode(); 327 } 328 } 329 330 return hashCode; 331 } 332 333 334 public String toString() 335 { 336 StringBuffer buffer = new StringBuffer(); 337 338 for (int index = 0; index < size; index++) 339 { 340 Value value = values[index]; 341 buffer = buffer.append('[') 342 .append(value == null ? "empty" : value.toString()) 343 .append(']'); 344 } 345 346 return buffer.toString(); 347 } 348} 349