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 an operand stack that contains <code>Value</code>
027: * objects.
028: *
029: * @author Eric Lafortune
030: */
031: public class Stack {
032: private static final TopValue TOP_VALUE = new TopValue();
033:
034: protected Value[] values;
035: protected int currentSize;
036: protected int actualMaxSize;
037:
038: /**
039: * Creates a new Stack with a given maximum size, accounting for the double
040: * space required by Category 2 values.
041: */
042: public Stack(int maxSize) {
043: values = new Value[maxSize];
044: }
045:
046: /**
047: * Creates a Stack that is a copy of the given Stack.
048: */
049: public Stack(Stack stack) {
050: // Create the values array.
051: this (stack.values.length);
052:
053: // Copy the stack contents.
054: copy(stack);
055: }
056:
057: /**
058: * Returns the actual maximum stack size that was required for all stack
059: * operations, accounting for the double space required by Category 2 values.
060: */
061: public int getActualMaxSize() {
062: return actualMaxSize;
063: }
064:
065: /**
066: * Resets this Stack, so that it can be reused.
067: */
068: public void reset(int maxSize) {
069: // Is the values array large enough?
070: if (maxSize > values.length) {
071: // Create a new one.
072: values = new Value[maxSize];
073: }
074:
075: // Clear the sizes.
076: clear();
077:
078: actualMaxSize = 0;
079: }
080:
081: /**
082: * Copies the values of the given Stack into this Stack.
083: */
084: public void copy(Stack other) {
085: // Is the values array large enough?
086: if (other.values.length > values.length) {
087: // Create a new one.
088: values = new Value[other.values.length];
089: }
090:
091: // Copy the stack contents.
092: System.arraycopy(other.values, 0, this .values, 0,
093: other.currentSize);
094:
095: // Copy the sizes.
096: currentSize = other.currentSize;
097: actualMaxSize = other.actualMaxSize;
098: }
099:
100: /**
101: * Generalizes the values of this Stack with the values of the given Stack.
102: * The stacks must have the same current sizes.
103: * @return whether the generalization has made any difference.
104: */
105: public boolean generalize(Stack other) {
106: if (this .currentSize != other.currentSize) {
107: throw new IllegalArgumentException(
108: "Stacks have different current sizes ["
109: + this .currentSize + "] and ["
110: + other.currentSize + "]");
111: }
112:
113: boolean changed = false;
114:
115: // Generalize the stack values.
116: for (int index = 0; index < currentSize; index++) {
117: Value this Value = this .values[index];
118:
119: if (this Value != null) {
120: Value newValue = null;
121:
122: Value otherValue = other.values[index];
123:
124: if (otherValue != null) {
125: newValue = this Value.generalize(otherValue);
126: }
127:
128: changed = changed || !this Value.equals(newValue);
129:
130: values[index] = newValue;
131: }
132: }
133:
134: // Check if the other stack extends beyond this one.
135: if (this .actualMaxSize < other.actualMaxSize) {
136: this .actualMaxSize = other.actualMaxSize;
137: }
138:
139: return changed;
140: }
141:
142: /**
143: * Clears the stack.
144: */
145: public void clear() {
146: // Clear the stack contents.
147: for (int index = 0; index < currentSize; index++) {
148: values[index] = null;
149: }
150:
151: currentSize = 0;
152: }
153:
154: /**
155: * Returns the number of elements currently on the stack, accounting for the
156: * double space required by Category 2 values.
157: */
158: public int size() {
159: return currentSize;
160: }
161:
162: /**
163: * Gets the specified Value from the stack, without disturbing it.
164: * @param index the index of the stack element, counting from the bottom
165: * of the stack.
166: * @return the value at the specified position.
167: */
168: public Value getBottom(int index) {
169: return values[index];
170: }
171:
172: /**
173: * Sets the specified Value on the stack, without disturbing it.
174: * @param index the index of the stack element, counting from the bottom
175: * of the stack.
176: * @param value the value to set.
177: */
178: public void setBottom(int index, Value value) {
179: values[index] = value;
180: }
181:
182: /**
183: * Gets the specified Value from the stack, without disturbing it.
184: * @param index the index of the stack element, counting from the top
185: * of the stack.
186: * @return the value at the specified position.
187: */
188: public Value getTop(int index) {
189: return values[currentSize - index - 1];
190: }
191:
192: /**
193: * Sets the specified Value on the stack, without disturbing it.
194: * @param index the index of the stack element, counting from the top
195: * of the stack.
196: * @param value the value to set.
197: */
198: public void setTop(int index, Value value) {
199: values[currentSize - index - 1] = value;
200: }
201:
202: /**
203: * Removes the specified Value from the stack.
204: * @param index the index of the stack element, counting from the top
205: * of the stack.
206: */
207: public void removeTop(int index) {
208: System.arraycopy(values, currentSize - index, values,
209: currentSize - index - 1, index);
210: currentSize--;
211: }
212:
213: /**
214: * Pushes the given Value onto the stack.
215: */
216: public void push(Value value) {
217: // Account for the extra space required by Category 2 values.
218: if (value.isCategory2()) {
219: values[currentSize++] = TOP_VALUE;
220: }
221:
222: // Push the value.
223: values[currentSize++] = value;
224:
225: // Update the maximum actual size;
226: if (actualMaxSize < currentSize) {
227: actualMaxSize = currentSize;
228: }
229: }
230:
231: /**
232: * Pops the top Value from the stack.
233: */
234: public Value pop() {
235: Value value = values[--currentSize];
236:
237: values[currentSize] = null;
238:
239: // Account for the extra space required by Category 2 values.
240: if (value.isCategory2()) {
241: values[--currentSize] = null;
242: }
243:
244: return value;
245: }
246:
247: // Pop methods that provide convenient casts to the expected value types.
248:
249: /**
250: * Pops the top IntegerValue from the stack.
251: */
252: public IntegerValue ipop() {
253: return pop().integerValue();
254: }
255:
256: /**
257: * Pops the top LongValue from the stack.
258: */
259: public LongValue lpop() {
260: return pop().longValue();
261: }
262:
263: /**
264: * Pops the top FloatValue from the stack.
265: */
266: public FloatValue fpop() {
267: return pop().floatValue();
268: }
269:
270: /**
271: * Pops the top DoubleValue from the stack.
272: */
273: public DoubleValue dpop() {
274: return pop().doubleValue();
275: }
276:
277: /**
278: * Pops the top ReferenceValue from the stack.
279: */
280: public ReferenceValue apop() {
281: return pop().referenceValue();
282: }
283:
284: /**
285: * Pops the top InstructionOffsetValue from the stack.
286: */
287: public InstructionOffsetValue opop() {
288: return pop().instructionOffsetValue();
289: }
290:
291: /**
292: * Pops the top category 1 value from the stack.
293: */
294: public void pop1() {
295: values[--currentSize] = null;
296: }
297:
298: /**
299: * Pops the top category 2 value from the stack (or alternatively, two
300: * Category 1 stack elements).
301: */
302: public void pop2() {
303: values[--currentSize] = null;
304: values[--currentSize] = null;
305: }
306:
307: /**
308: * Duplicates the top Category 1 value.
309: */
310: public void dup() {
311: values[currentSize] = values[currentSize - 1].category1Value();
312:
313: currentSize++;
314:
315: // Update the maximum actual size;
316: if (actualMaxSize < currentSize) {
317: actualMaxSize = currentSize;
318: }
319: }
320:
321: /**
322: * Duplicates the top Category 1 value, one Category 1 element down the
323: * stack.
324: */
325: public void dup_x1() {
326: values[currentSize] = values[currentSize - 1].category1Value();
327: values[currentSize - 1] = values[currentSize - 2]
328: .category1Value();
329: values[currentSize - 2] = values[currentSize];
330:
331: currentSize++;
332:
333: // Update the maximum actual size;
334: if (actualMaxSize < currentSize) {
335: actualMaxSize = currentSize;
336: }
337: }
338:
339: /**
340: * Duplicates the top Category 1 value, two Category 1 elements (or one
341: * Category 2 element) down the stack.
342: */
343: public void dup_x2() {
344: values[currentSize] = values[currentSize - 1].category1Value();
345: values[currentSize - 1] = values[currentSize - 2];
346: values[currentSize - 2] = values[currentSize - 3];
347: values[currentSize - 3] = values[currentSize];
348:
349: currentSize++;
350:
351: // Update the maximum actual size;
352: if (actualMaxSize < currentSize) {
353: actualMaxSize = currentSize;
354: }
355: }
356:
357: /**
358: * Duplicates the top Category 2 value (or alternatively, the equivalent
359: * Category 1 stack elements).
360: */
361: public void dup2() {
362: values[currentSize] = values[currentSize - 2];
363: values[currentSize + 1] = values[currentSize - 1];
364:
365: currentSize += 2;
366:
367: // Update the maximum actual size;
368: if (actualMaxSize < currentSize) {
369: actualMaxSize = currentSize;
370: }
371: }
372:
373: /**
374: * Duplicates the top Category 2 value, one Category 1 element down the
375: * stack (or alternatively, the equivalent Category 1 stack values).
376: */
377: public void dup2_x1() {
378: values[currentSize + 1] = values[currentSize - 1];
379: values[currentSize] = values[currentSize - 2];
380: values[currentSize - 1] = values[currentSize - 3];
381: values[currentSize - 2] = values[currentSize + 1];
382: values[currentSize - 3] = values[currentSize];
383:
384: currentSize += 2;
385:
386: // Update the maximum actual size;
387: if (actualMaxSize < currentSize) {
388: actualMaxSize = currentSize;
389: }
390: }
391:
392: /**
393: * Duplicates the top Category 2 value, one Category 2 stack element down
394: * the stack (or alternatively, the equivalent Category 1 stack values).
395: */
396: public void dup2_x2() {
397: values[currentSize + 1] = values[currentSize - 1];
398: values[currentSize] = values[currentSize - 2];
399: values[currentSize - 1] = values[currentSize - 3];
400: values[currentSize - 2] = values[currentSize - 4];
401: values[currentSize - 3] = values[currentSize + 1];
402: values[currentSize - 4] = values[currentSize];
403:
404: currentSize += 2;
405:
406: // Update the maximum actual size;
407: if (actualMaxSize < currentSize) {
408: actualMaxSize = currentSize;
409: }
410: }
411:
412: /**
413: * Swaps the top two Category 1 values.
414: */
415: public void swap() {
416: Value value1 = values[currentSize - 1].category1Value();
417: Value value2 = values[currentSize - 2].category1Value();
418:
419: values[currentSize - 1] = value2;
420: values[currentSize - 2] = value1;
421: }
422:
423: // Implementations for Object.
424:
425: public boolean equals(Object object) {
426: if (object == null || this .getClass() != object.getClass()) {
427: return false;
428: }
429:
430: Stack other = (Stack) object;
431:
432: if (this .currentSize != other.currentSize) {
433: return false;
434: }
435:
436: for (int index = 0; index < currentSize; index++) {
437: Value this Value = this .values[index];
438: Value otherValue = other.values[index];
439: if (this Value == null ? otherValue != null : !this Value
440: .equals(otherValue)) {
441: return false;
442: }
443: }
444:
445: return true;
446: }
447:
448: public int hashCode() {
449: int hashCode = currentSize;
450:
451: for (int index = 0; index < currentSize; index++) {
452: Value value = values[index];
453: if (value != null) {
454: hashCode ^= value.hashCode();
455: }
456: }
457:
458: return hashCode;
459: }
460:
461: public String toString() {
462: StringBuffer buffer = new StringBuffer();
463:
464: for (int index = 0; index < currentSize; index++) {
465: Value value = values[index];
466: buffer = buffer.append('[').append(
467: value == null ? "empty" : value.toString()).append(
468: ']');
469: }
470:
471: return buffer.toString();
472: }
473: }
|