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