/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2011 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package proguard.evaluation; import proguard.evaluation.value.*; import java.util.Arrays; /** * This class represents a local variable frame that contains <code>Value</code> * objects. Values are generalizations of all values that have been stored in * the respective variables. * * @author Eric Lafortune */ public class Variables { private static final TopValue TOP_VALUE = new TopValue(); protected Value[] values; protected int size; /** * Creates a new Variables object with a given maximum number of variables. */ public Variables(int size) { this.values = new Value[size]; this.size = size; } /** * Creates a Variables object that is a copy of the given Variables object. */ public Variables(Variables variables) { // Create the values array. this(variables.size); // Copy the values. initialize(variables); } /** * Resets this Variables object, so that it can be reused. */ public void reset(int size) { // Is the values array large enough? if (size > values.length) { // Create a new one. values = new Value[size]; } else { // Clear the variables. Arrays.fill(values, null); } this.size = size; } /** * Initializes the values of this Variables object with the values of the * given Variables object. The other object may have fewer values, in which * case the remaining values are left unchanged. */ public void initialize(Variables other) { if (this.size < other.size) { throw new IllegalArgumentException("Variable frame is too small ["+this.size+"] compared to other frame ["+other.size+"]"); } // Copy the values. System.arraycopy(other.values, 0, this.values, 0, other.size); } /** * Generalizes the values of this Variables object with the values of the * given Variables object. * @param clearConflictingOtherVariables specifies whether the other * variables should be cleared too, * in case of conflicts. * @return whether the generalization has made any difference. */ public boolean generalize(Variables other, boolean clearConflictingOtherVariables) { if (this.size != other.size) { throw new IllegalArgumentException("Variable frames have different sizes ["+this.size+"] and ["+other.size+"]"); } boolean changed = false; for (int index = 0; index < size; index++) { Value thisValue = this.values[index]; Value otherValue = other.values[index]; // Occasionally, two values of different types might be present // in the same variable in a variable frame (corresponding to // two local variables that share the same index), at some point // outside of their scopes. Don't generalize the variable then, // but let it clear instead. if (thisValue != null && otherValue != null && thisValue.computationalType() == otherValue.computationalType()) { Value newValue = thisValue.generalize(otherValue); changed = changed || !thisValue.equals(newValue); this.values[index] = newValue; } else { changed = changed || thisValue != null; this.values[index] = null; if (clearConflictingOtherVariables) { other.values[index] = null; } } } return changed; } /** * Returns the number of variables. */ public int size() { return size; } /** * Gets the Value of the variable with the given index, without disturbing it. */ public Value getValue(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); } return values[index]; } /** * Stores the given Value at the given variable index. */ public void store(int index, Value value) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); } // Store the value. values[index] = value; // Account for the extra space required by Category 2 values. if (value.isCategory2()) { values[index + 1] = TOP_VALUE; } } /** * Loads the Value from the variable with the given index. */ public Value load(int index) { if (index < 0 || index >= size) { throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]"); } return values[index]; } // Load methods that provide convenient casts to the expected value types. /** * Loads the IntegerValue from the variable with the given index. */ public IntegerValue iload(int index) { return load(index).integerValue(); } /** * Loads the LongValue from the variable with the given index. */ public LongValue lload(int index) { return load(index).longValue(); } /** * Loads the FloatValue from the variable with the given index. */ public FloatValue fload(int index) { return load(index).floatValue(); } /** * Loads the DoubleValue from the variable with the given index. */ public DoubleValue dload(int index) { return load(index).doubleValue(); } /** * Loads the ReferenceValue from the variable with the given index. */ public ReferenceValue aload(int index) { return load(index).referenceValue(); } /** * Loads the InstructionOffsetValue from the variable with the given index. */ public InstructionOffsetValue oload(int index) { return load(index).instructionOffsetValue(); } // Implementations for Object. public boolean equals(Object object) { if (object == null || this.getClass() != object.getClass()) { return false; } Variables other = (Variables)object; if (this.size != other.size) { return false; } for (int index = 0; index < size; index++) { Value thisValue = this.values[index]; Value otherValue = other.values[index]; // Occasionally, two values of different types might be // present in the same variable in a variable frame // (corresponding to two local variables that share the // same index), at some point outside of their scopes. // We'll ignore these. if (thisValue != null && otherValue != null && thisValue.computationalType() == otherValue.computationalType() && !thisValue.equals(otherValue)) { return false; } } return true; } public int hashCode() { int hashCode = size; for (int index = 0; index < size; index++) { Value value = values[index]; if (value != null) { hashCode ^= value.hashCode(); } } return hashCode; } public String toString() { StringBuffer buffer = new StringBuffer(); for (int index = 0; index < size; index++) { Value value = values[index]; buffer = buffer.append('[') .append(value == null ? "empty" : value.toString()) .append(']'); } return buffer.toString(); } }