proguard.evaluation.Stack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-core Show documentation
Show all versions of proguard-core Show documentation
ProGuardCORE is a free library to read, analyze, modify, and write Java class files.
/*
* ProGuardCORE -- library to process Java bytecode.
*
* Copyright (c) 2002-2020 Guardsquare NV
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package proguard.evaluation;
import java.util.Arrays;
import proguard.classfile.TypeConstants;
import proguard.evaluation.exception.StackCategoryOneException;
import proguard.evaluation.exception.StackTypeException;
import proguard.evaluation.value.*;
/**
* This class represents an operand stack that contains {@link Value} instances.
*
* @author Eric Lafortune
*/
public class Stack {
private static final TopValue TOP_VALUE = new TopValue();
protected Value[] values;
protected int currentSize;
protected int actualMaxSize;
/**
* Creates a new Stack with a given maximum size, accounting for the double space required by
* Category 2 values.
*/
public Stack(int maxSize) {
values = new Value[maxSize];
}
/** Creates a Stack that is a copy of the given Stack. */
public Stack(Stack stack) {
// Create the values array.
this(stack.values.length);
// Copy the stack contents.
copy(stack);
}
/**
* Returns the actual maximum stack size that was required for all stack operations, accounting
* for the double space required by Category 2 values.
*/
public int getActualMaxSize() {
return actualMaxSize;
}
/** Resets this Stack, so that it can be reused. */
public void reset(int maxSize) {
// Is the values array large enough?
if (values.length < maxSize) {
// Create a new one.
values = new Value[maxSize];
}
// Clear the sizes.
clear();
actualMaxSize = 0;
}
/** Copies the values of the given Stack into this Stack. */
public void copy(Stack other) {
// Is the values array large enough?
if (values.length < other.values.length) {
// Create a new one.
values = new Value[other.values.length];
}
// Copy the stack contents.
System.arraycopy(other.values, 0, this.values, 0, other.currentSize);
// Copy the sizes.
currentSize = other.currentSize;
actualMaxSize = other.actualMaxSize;
}
/**
* Generalizes the values of this Stack with the values of the given Stack. The stacks must have
* the same current sizes.
*
* @return whether the generalization has made any difference.
*/
public boolean generalize(Stack other) {
if (this.currentSize != other.currentSize) {
throw new IllegalArgumentException(
"Stacks have different current sizes ["
+ this.currentSize
+ "] and ["
+ other.currentSize
+ "]");
}
boolean changed = false;
// Generalize the stack values.
for (int index = 0; index < currentSize; index++) {
Value thisValue = this.values[index];
if (thisValue != null) {
Value newValue = null;
Value otherValue = other.values[index];
if (otherValue != null) {
newValue = thisValue.generalize(otherValue);
}
changed = changed || !thisValue.equals(newValue);
values[index] = newValue;
}
}
// Check if the other stack extends beyond this one.
if (this.actualMaxSize < other.actualMaxSize) {
this.actualMaxSize = other.actualMaxSize;
}
return changed;
}
/** Clears the stack. */
public void clear() {
// Clear the stack contents.
Arrays.fill(values, 0, currentSize, null);
currentSize = 0;
}
/**
* Returns the number of elements currently on the stack, accounting for the double space required
* by Category 2 values.
*/
public int size() {
return currentSize;
}
/**
* Gets the specified Value from the stack, without disturbing it.
*
* @param index the index of the stack element, counting from the bottom of the stack.
* @return the value at the specified position.
*/
public Value getBottom(int index) {
return values[index];
}
/**
* Sets the specified Value on the stack, without disturbing it.
*
* @param index the index of the stack element, counting from the bottom of the stack.
* @param value the value to set.
*/
public void setBottom(int index, Value value) {
values[index] = value;
}
/**
* Gets the specified Value from the stack, without disturbing it.
*
* @param index the index of the stack element, counting from the top of the stack.
* @return the value at the specified position.
*/
public Value getTop(int index) {
return values[currentSize - index - 1];
}
/**
* Sets the specified Value on the stack, without disturbing it.
*
* @param index the index of the stack element, counting from the top of the stack.
* @param value the value to set.
*/
public void setTop(int index, Value value) {
values[currentSize - index - 1] = value;
}
/**
* Removes the specified Value from the stack.
*
* @param index the index of the stack element, counting from the top of the stack.
*/
public void removeTop(int index) {
System.arraycopy(values, currentSize - index, values, currentSize - index - 1, index);
currentSize--;
}
/** Pushes the given Value onto the stack. */
public void push(Value value) {
// Account for the extra space required by Category 2 values.
if (value.isCategory2()) {
values[currentSize++] = TOP_VALUE;
}
// Push the value.
values[currentSize++] = value;
// Update the maximum actual size;
if (actualMaxSize < currentSize) {
actualMaxSize = currentSize;
}
}
/** Pops the top Value from the stack. */
public Value pop() {
Value value = values[--currentSize];
values[currentSize] = null;
// Account for the extra space required by Category 2 values.
if (value.isCategory2()) {
values[--currentSize] = null;
}
return value;
}
// Pop methods that provide convenient casts to the expected value types.
/** Pops the top IntegerValue from the stack. */
public IntegerValue ipop() {
Value val = pop();
try {
return val.integerValue();
} catch (IllegalArgumentException ex) {
throw new StackTypeException(val, Character.toString(TypeConstants.INT), ex);
}
}
/** Pops the top LongValue from the stack. */
public LongValue lpop() {
Value val = pop();
try {
return val.longValue();
} catch (IllegalArgumentException ex) {
throw new StackTypeException(val, Character.toString(TypeConstants.LONG), ex);
}
}
/** Pops the top FloatValue from the stack. */
public FloatValue fpop() {
Value val = pop();
try {
return val.floatValue();
} catch (IllegalArgumentException ex) {
throw new StackTypeException(val, Character.toString(TypeConstants.FLOAT), ex);
}
}
/** Pops the top DoubleValue from the stack. */
public DoubleValue dpop() {
Value val = pop();
try {
return val.doubleValue();
} catch (IllegalArgumentException ex) {
throw new StackTypeException(val, Character.toString(TypeConstants.DOUBLE), ex);
}
}
/** Pops the top ReferenceValue from the stack. */
public ReferenceValue apop() {
Value val = pop();
try {
return val.referenceValue();
} catch (IllegalArgumentException ex) {
throw new StackTypeException(
val,
String.format("%cSOME_REFERENCE%c", TypeConstants.CLASS_START, TypeConstants.CLASS_END),
ex);
}
}
/** Pops the top InstructionOffsetValue from the stack. */
public InstructionOffsetValue opop() {
return pop().instructionOffsetValue();
}
/** Pops the top category 1 value from the stack. */
public void pop1() {
values[--currentSize] = null;
}
/**
* Pops the top category 2 value from the stack (or alternatively, two Category 1 stack elements).
*/
public void pop2() {
values[--currentSize] = null;
values[--currentSize] = null;
}
private Category1Value getSafeCategory1Value(Value value) {
try {
return value.category1Value();
} catch (IllegalArgumentException ex) {
throw new StackCategoryOneException(value, ex);
}
}
/** Duplicates the top Category 1 value. */
public void dup() {
values[currentSize] = getSafeCategory1Value(values[currentSize - 1]);
currentSize++;
// Update the maximum actual size;
if (actualMaxSize < currentSize) {
actualMaxSize = currentSize;
}
}
/** Duplicates the top Category 1 value, one Category 1 element down the stack. */
public void dup_x1() {
values[currentSize] = getSafeCategory1Value(values[currentSize - 1]);
values[currentSize - 1] = getSafeCategory1Value(values[currentSize - 2]);
values[currentSize - 2] = values[currentSize];
currentSize++;
// Update the maximum actual size;
if (actualMaxSize < currentSize) {
actualMaxSize = currentSize;
}
}
/**
* Duplicates the top Category 1 value, two Category 1 elements (or one Category 2 element) down
* the stack.
*/
public void dup_x2() {
values[currentSize] = getSafeCategory1Value(values[currentSize - 1]);
values[currentSize - 1] = values[currentSize - 2];
values[currentSize - 2] = values[currentSize - 3];
values[currentSize - 3] = values[currentSize];
currentSize++;
// Update the maximum actual size;
if (actualMaxSize < currentSize) {
actualMaxSize = currentSize;
}
}
/**
* Duplicates the top Category 2 value (or alternatively, the equivalent Category 1 stack
* elements).
*/
public void dup2() {
values[currentSize] = values[currentSize - 2];
values[currentSize + 1] = values[currentSize - 1];
currentSize += 2;
// Update the maximum actual size;
if (actualMaxSize < currentSize) {
actualMaxSize = currentSize;
}
}
/**
* Duplicates the top Category 2 value, one Category 1 element down the stack (or alternatively,
* the equivalent Category 1 stack values).
*/
public void dup2_x1() {
values[currentSize + 1] = values[currentSize - 1];
values[currentSize] = values[currentSize - 2];
values[currentSize - 1] = values[currentSize - 3];
values[currentSize - 2] = values[currentSize + 1];
values[currentSize - 3] = values[currentSize];
currentSize += 2;
// Update the maximum actual size;
if (actualMaxSize < currentSize) {
actualMaxSize = currentSize;
}
}
/**
* Duplicates the top Category 2 value, one Category 2 stack element down the stack (or
* alternatively, the equivalent Category 1 stack values).
*/
public void dup2_x2() {
values[currentSize + 1] = values[currentSize - 1];
values[currentSize] = values[currentSize - 2];
values[currentSize - 1] = values[currentSize - 3];
values[currentSize - 2] = values[currentSize - 4];
values[currentSize - 3] = values[currentSize + 1];
values[currentSize - 4] = values[currentSize];
currentSize += 2;
// Update the maximum actual size;
if (actualMaxSize < currentSize) {
actualMaxSize = currentSize;
}
}
/** Swaps the top two Category 1 values. */
public void swap() {
Value value1 = getSafeCategory1Value(values[currentSize - 1]);
Value value2 = getSafeCategory1Value(values[currentSize - 2]);
values[currentSize - 1] = value2;
values[currentSize - 2] = value1;
}
/** Replaces all the references to {@param toReplace} with references to {@param replacement}. */
public void replaceReferences(Value toReplace, Value replacement) {
for (int i = 0; i < values.length; i++) {
if (values[i] == toReplace) {
values[i] = replacement;
}
}
}
// Implementations for Object.
public boolean equals(Object object) {
if (object == null || this.getClass() != object.getClass()) {
return false;
}
Stack other = (Stack) object;
if (this.currentSize != other.currentSize) {
return false;
}
for (int index = 0; index < currentSize; index++) {
Value thisValue = this.values[index];
Value otherValue = other.values[index];
if (thisValue == null ? otherValue != null : !thisValue.equals(otherValue)) {
return false;
}
}
return true;
}
public int hashCode() {
int hashCode = currentSize;
for (int index = 0; index < currentSize; 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 < currentSize; index++) {
Value value = values[index];
buffer = buffer.append('[').append(value == null ? "empty" : value.toString()).append(']');
}
return buffer.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy