001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.evaluation;
022:
023: import proguard.evaluation.value.*;
024:
025: /**
026: * This class represents a local variable frame that contains <code>Value</code>
027: * objects. Values are generalizations of all values that have been stored in
028: * the respective variables.
029: *
030: * @author Eric Lafortune
031: */
032: public class Variables {
033: private static final TopValue TOP_VALUE = new TopValue();
034:
035: protected Value[] values;
036: protected int size;
037:
038: /**
039: * Creates a new Variables object with a given maximum number of variables.
040: */
041: public Variables(int size) {
042: this .values = new Value[size];
043: this .size = size;
044: }
045:
046: /**
047: * Creates a Variables object that is a copy of the given Variables object.
048: */
049: public Variables(Variables variables) {
050: // Create the values array.
051: this (variables.size);
052:
053: // Copy the values.
054: initialize(variables);
055: }
056:
057: /**
058: * Resets this Variables object, so that it can be reused.
059: */
060: public void reset(int size) {
061: // Is the values array large enough?
062: if (size > values.length) {
063: // Create a new one.
064: values = new Value[size];
065: } else {
066: // Clear the variables.
067: for (int index = 0; index < values.length; index++) {
068: values[index] = null;
069: }
070: }
071:
072: this .size = size;
073: }
074:
075: /**
076: * Initializes the values of this Variables object with the values of the
077: * given Variables object. The other object may have fewer values, in which
078: * case the remaining values are left unchanged.
079: */
080: public void initialize(Variables other) {
081: if (this .size < other.size) {
082: throw new IllegalArgumentException(
083: "Variable frame is too small [" + this .size
084: + "] compared to other frame ["
085: + other.size + "]");
086: }
087:
088: // Copy the values.
089: System.arraycopy(other.values, 0, this .values, 0, other.size);
090: }
091:
092: /**
093: * Generalizes the values of this Variables object with the values of the
094: * given Variables object.
095: * @param clearConflictingOtherVariables specifies whether the other
096: * variables should be cleared too,
097: * in case of conflicts.
098: * @return whether the generalization has made any difference.
099: */
100: public boolean generalize(Variables other,
101: boolean clearConflictingOtherVariables) {
102: if (this .size != other.size) {
103: throw new IllegalArgumentException(
104: "Variable frames have different sizes ["
105: + this .size + "] and [" + other.size + "]");
106: }
107:
108: boolean changed = false;
109:
110: for (int index = 0; index < size; index++) {
111: Value this Value = this .values[index];
112: Value otherValue = other.values[index];
113:
114: // Occasionally, two values of different types might be present
115: // in the same variable in a variable frame (corresponding to
116: // two local variables that share the same index), at some point
117: // outside of their scopes. Don't generalize the variable then,
118: // but let it clear instead.
119: if (this Value != null
120: && otherValue != null
121: && this Value.computationalType() == otherValue
122: .computationalType()) {
123: Value newValue = this Value.generalize(otherValue);
124:
125: changed = changed || !this Value.equals(newValue);
126:
127: this .values[index] = newValue;
128: } else {
129: changed = changed || this Value != null;
130:
131: this .values[index] = null;
132:
133: if (clearConflictingOtherVariables) {
134: other.values[index] = null;
135: }
136: }
137: }
138:
139: return changed;
140: }
141:
142: /**
143: * Returns the number of variables.
144: */
145: public int size() {
146: return size;
147: }
148:
149: /**
150: * Gets the Value of the variable with the given index, without disturbing it.
151: */
152: public Value getValue(int index) {
153: if (index < 0 || index >= size) {
154: throw new IndexOutOfBoundsException("Variable index ["
155: + index + "] out of bounds [" + size + "]");
156: }
157:
158: return values[index];
159: }
160:
161: /**
162: * Stores the given Value at the given variable index.
163: */
164: public void store(int index, Value value) {
165: if (index < 0 || index >= size) {
166: throw new IndexOutOfBoundsException("Variable index ["
167: + index + "] out of bounds [" + size + "]");
168: }
169:
170: // Store the value.
171: values[index] = value;
172:
173: // Account for the extra space required by Category 2 values.
174: if (value.isCategory2()) {
175: values[index + 1] = TOP_VALUE;
176: }
177: }
178:
179: /**
180: * Loads the Value from the variable with the given index.
181: */
182: public Value load(int index) {
183: if (index < 0 || index >= size) {
184: throw new IndexOutOfBoundsException("Variable index ["
185: + index + "] out of bounds [" + size + "]");
186: }
187:
188: return values[index];
189: }
190:
191: // Load methods that provide convenient casts to the expected value types.
192:
193: /**
194: * Loads the IntegerValue from the variable with the given index.
195: */
196: public IntegerValue iload(int index) {
197: return load(index).integerValue();
198: }
199:
200: /**
201: * Loads the LongValue from the variable with the given index.
202: */
203: public LongValue lload(int index) {
204: return load(index).longValue();
205: }
206:
207: /**
208: * Loads the FloatValue from the variable with the given index.
209: */
210: public FloatValue fload(int index) {
211: return load(index).floatValue();
212: }
213:
214: /**
215: * Loads the DoubleValue from the variable with the given index.
216: */
217: public DoubleValue dload(int index) {
218: return load(index).doubleValue();
219: }
220:
221: /**
222: * Loads the ReferenceValue from the variable with the given index.
223: */
224: public ReferenceValue aload(int index) {
225: return load(index).referenceValue();
226: }
227:
228: /**
229: * Loads the InstructionOffsetValue from the variable with the given index.
230: */
231: public InstructionOffsetValue oload(int index) {
232: return load(index).instructionOffsetValue();
233: }
234:
235: // Implementations for Object.
236:
237: public boolean equals(Object object) {
238: if (object == null || this .getClass() != object.getClass()) {
239: return false;
240: }
241:
242: Variables other = (Variables) object;
243:
244: if (this .size != other.size) {
245: return false;
246: }
247:
248: for (int index = 0; index < size; index++) {
249: Value this Value = this .values[index];
250: Value otherValue = other.values[index];
251:
252: // Occasionally, two values of different types might be
253: // present in the same variable in a variable frame
254: // (corresponding to two local variables that share the
255: // same index), at some point outside of their scopes.
256: // We'll ignore these.
257: if (this Value != null
258: && otherValue != null
259: && this Value.computationalType() == otherValue
260: .computationalType()
261: && !this Value.equals(otherValue)) {
262: return false;
263: }
264: }
265:
266: return true;
267: }
268:
269: public int hashCode() {
270: int hashCode = size;
271:
272: for (int index = 0; index < size; index++) {
273: Value value = values[index];
274: if (value != null) {
275: hashCode ^= value.hashCode();
276: }
277: }
278:
279: return hashCode;
280: }
281:
282: public String toString() {
283: StringBuffer buffer = new StringBuffer();
284:
285: for (int index = 0; index < size; index++) {
286: Value value = values[index];
287: buffer = buffer.append('[').append(
288: value == null ? "empty" : value.toString()).append(
289: ']');
290: }
291:
292: return buffer.toString();
293: }
294: }
|