/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.bytecode.analysis;
/**
* Represents the stack frame and local variable table at a particular point in time.
*
* @author Jason T. Greene
*/
public class
Frame {
private
Type[]
locals;
private
Type[]
stack;
private int
top;
private boolean
jsrMerged;
private boolean
retMerged;
/**
* Create a new frame with the specified local variable table size, and max stack size
*
* @param locals the number of local variable table entries
* @param stack the maximum stack size
*/
public
Frame(int
locals, int
stack) {
this.
locals = new
Type[
locals];
this.
stack = new
Type[
stack];
}
/**
* Returns the local varaible table entry at index.
*
* @param index the position in the table
* @return the type if one exists, or null if the position is empty
*/
public
Type getLocal(int
index) {
return
locals[
index];
}
/**
* Sets the local variable table entry at index to a type.
*
* @param index the position in the table
* @param type the type to set at the position
*/
public void setLocal(int
index,
Type type) {
locals[
index] =
type;
}
/**
* Returns the type on the stack at the specified index.
*
* @param index the position on the stack
* @return the type of the stack position
*/
public
Type getStack(int
index) {
return
stack[
index];
}
/**
* Sets the type of the stack position
*
* @param index the position on the stack
* @param type the type to set
*/
public void setStack(int
index,
Type type) {
stack[
index] =
type;
}
/**
* Empties the stack
*/
public void
clearStack() {
top = 0;
}
/**
* Gets the index of the type sitting at the top of the stack.
* This is not to be confused with a length operation which
* would return the number of elements, not the position of
* the last element.
*
* @return the position of the element at the top of the stack
*/
public int
getTopIndex() {
return
top - 1;
}
/**
* Returns the number of local variable table entries, specified
* at construction.
*
* @return the number of local variable table entries
*/
public int
localsLength() {
return
locals.
length;
}
/**
* Gets the top of the stack without altering it
*
* @return the top of the stack
*/
public
Type peek() {
if (
top < 1)
throw new
IndexOutOfBoundsException("Stack is empty");
return
stack[
top - 1];
}
/**
* Alters the stack to contain one less element and return it.
*
* @return the element popped from the stack
*/
public
Type pop() {
if (
top < 1)
throw new
IndexOutOfBoundsException("Stack is empty");
return
stack[--
top];
}
/**
* Alters the stack by placing the passed type on the top
*
* @param type the type to add to the top
*/
public void push(
Type type) {
stack[
top++] =
type;
}
/**
* Makes a shallow copy of this frame, i.e. the type instances will
* remain the same.
*
* @return the shallow copy
*/
public
Frame copy() {
Frame frame = new
Frame(
locals.
length,
stack.
length);
System.
arraycopy(
locals, 0,
frame.
locals, 0,
locals.
length);
System.
arraycopy(
stack, 0,
frame.
stack, 0,
stack.
length);
frame.
top =
top;
return
frame;
}
/**
* Makes a shallow copy of the stack portion of this frame. The local
* variable table size will be copied, but its contents will be empty.
*
* @return the shallow copy of the stack
*/
public
Frame copyStack() {
Frame frame = new
Frame(
locals.
length,
stack.
length);
System.
arraycopy(
stack, 0,
frame.
stack, 0,
stack.
length);
frame.
top =
top;
return
frame;
}
/**
* Merges all types on the stack of this frame instance with that of the specified frame.
* The local variable table is left untouched.
*
* @param frame the frame to merge the stack from
* @return true if any changes where made
*/
public boolean
mergeStack(
Frame frame) {
boolean
changed = false;
if (
top !=
frame.
top)
throw new
RuntimeException("Operand stacks could not be merged, they are different sizes!");
for (int
i = 0;
i <
top;
i++) {
if (
stack[
i] != null) {
Type prev =
stack[
i];
Type merged =
prev.
merge(
frame.
stack[
i]);
if (
merged ==
Type.
BOGUS)
throw new
RuntimeException("Operand stacks could not be merged due to differing primitive types: pos = " +
i);
stack[
i] =
merged;
// always replace the instance in case a multi-interface type changes to a normal Type
if ((!
merged.
equals(
prev)) ||
merged.
popChanged()) {
changed = true;
}
}
}
return
changed;
}
/**
* Merges all types on the stack and local variable table of this frame with that of the specified
* type.
*
* @param frame the frame to merge with
* @return true if any changes to this frame where made by this merge
*/
public boolean
merge(
Frame frame) {
boolean
changed = false;
// Local variable table
for (int
i = 0;
i <
locals.
length;
i++) {
if (
locals[
i] != null) {
Type prev =
locals[
i];
Type merged =
prev.
merge(
frame.
locals[
i]);
// always replace the instance in case a multi-interface type changes to a normal Type
locals[
i] =
merged;
if (!
merged.
equals(
prev) ||
merged.
popChanged()) {
changed = true;
}
} else if (
frame.
locals[
i] != null) {
locals[
i] =
frame.
locals[
i];
changed = true;
}
}
changed |=
mergeStack(
frame);
return
changed;
}
public
String toString() {
StringBuffer buffer = new
StringBuffer();
buffer.
append("locals = [");
for (int
i = 0;
i <
locals.
length;
i++) {
buffer.
append(
locals[
i] == null ? "empty" :
locals[
i].
toString());
if (
i <
locals.
length - 1)
buffer.
append(", ");
}
buffer.
append("] stack = [");
for (int
i = 0;
i <
top;
i++) {
buffer.
append(
stack[
i]);
if (
i <
top - 1)
buffer.
append(", ");
}
buffer.
append("]");
return
buffer.
toString();
}
/**
* Whether or not state from the source JSR instruction has been merged
*
* @return true if JSR state has been merged
*/
boolean
isJsrMerged() {
return
jsrMerged;
}
/**
* Sets whether of not the state from the source JSR instruction has been merged
*
* @param jsrMerged true if merged, otherwise false
*/
void
setJsrMerged(boolean
jsrMerged) {
this.
jsrMerged =
jsrMerged;
}
/**
* Whether or not state from the RET instruction, of the subroutine that was jumped
* to has been merged.
*
* @return true if RET state has been merged
*/
boolean
isRetMerged() {
return
retMerged;
}
/**
* Sets whether or not state from the RET instruction, of the subroutine that was jumped
* to has been merged.
*
* @param retMerged true if RET state has been merged
*/
void
setRetMerged(boolean
retMerged) {
this.
retMerged =
retMerged;
}
}