blob: 614e03edb2efcdd337f7ce509f6fbbaa3d475511 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.codegen;
import java.text.MessageFormat;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
public class StackMapFrame implements Cloneable {
public static final int USED = 1;
public static final int SAME_FRAME = 0;
public static final int CHOP_FRAME = 1;
public static final int APPEND_FRAME = 2;
public static final int SAME_FRAME_EXTENDED = 3;
public static final int FULL_FRAME = 4;
public static final int SAME_LOCALS_1_STACK_ITEMS = 5;
public static final int SAME_LOCALS_1_STACK_ITEMS_EXTENDED = 6;
public int pc;
public int numberOfStackItems;
private int numberOfLocals;
public int localIndex;
public VerificationTypeInfo[] locals;
public VerificationTypeInfo[] stackItems;
private int numberOfDifferentLocals = -1;
public int tagBits;
public StackMapFrame() {
this.numberOfLocals = -1;
this.numberOfDifferentLocals = -1;
}
public int getFrameType(StackMapFrame prevFrame) {
final int offsetDelta = this.getOffsetDelta(prevFrame);
switch(this.numberOfStackItems) {
case 0 :
switch(this.numberOfDifferentLocals(prevFrame)) {
case 0 :
return offsetDelta <= 63 ? SAME_FRAME : SAME_FRAME_EXTENDED;
case 1 :
case 2 :
case 3 :
return APPEND_FRAME;
case -1 :
case -2 :
case -3 :
return CHOP_FRAME;
}
break;
case 1 :
switch(this.numberOfDifferentLocals(prevFrame)) {
case 0 :
return offsetDelta <= 63 ? SAME_LOCALS_1_STACK_ITEMS : SAME_LOCALS_1_STACK_ITEMS_EXTENDED;
}
}
return FULL_FRAME;
}
public void addLocal(int resolvedPosition, VerificationTypeInfo info) {
if (this.locals == null) {
this.locals = new VerificationTypeInfo[resolvedPosition + 1];
this.locals[resolvedPosition] = info;
} else {
final int length = this.locals.length;
if (resolvedPosition >= length) {
System.arraycopy(this.locals, 0, this.locals = new VerificationTypeInfo[resolvedPosition + 1], 0, length);
}
this.locals[resolvedPosition] = info;
}
}
public void addStackItem(VerificationTypeInfo info) {
if (info == null) return;
if (this.stackItems == null) {
this.stackItems = new VerificationTypeInfo[1];
this.stackItems[0] = info;
this.numberOfStackItems = 1;
} else {
final int length = this.stackItems.length;
if (this.numberOfStackItems == length) {
System.arraycopy(this.stackItems, 0, this.stackItems = new VerificationTypeInfo[length + 1], 0, length);
}
this.stackItems[this.numberOfStackItems++] = info;
}
}
public void addStackItem(TypeBinding binding) {
this.addStackItem(new VerificationTypeInfo(binding));
}
public Object clone() throws CloneNotSupportedException {
StackMapFrame result = (StackMapFrame) super.clone();
result.numberOfLocals = -1;
result.numberOfDifferentLocals = -1;
result.pc = this.pc;
result.numberOfStackItems = this.numberOfStackItems;
int length = this.locals == null ? 0 : this.locals.length;
if (length != 0) {
result.locals = new VerificationTypeInfo[length];
for (int i = 0; i < length; i++) {
final VerificationTypeInfo verificationTypeInfo = this.locals[i];
if (verificationTypeInfo != null) {
result.locals[i] = (VerificationTypeInfo) verificationTypeInfo.clone();
}
}
}
length = this.numberOfStackItems;
if (length != 0) {
result.stackItems = new VerificationTypeInfo[length];
for (int i = 0; i < length; i++) {
result.stackItems[i] = (VerificationTypeInfo) this.stackItems[i].clone();
}
}
return result;
}
public int numberOfDiffentStackItems(StackMapFrame prevFrame) {
if (prevFrame == null) {
return this.numberOfStackItems;
}
return this.numberOfStackItems - prevFrame.numberOfStackItems;
}
public int numberOfDifferentLocals(StackMapFrame prevFrame) {
if (this.numberOfDifferentLocals != -1) return this.numberOfDifferentLocals;
if (prevFrame == null) {
this.numberOfDifferentLocals = 0;
return 0;
}
VerificationTypeInfo[] prevLocals = prevFrame.locals;
VerificationTypeInfo[] currentLocals = this.locals;
int prevLocalsLength = prevLocals == null ? 0 : prevLocals.length;
int currentLocalsLength = currentLocals == null ? 0 : currentLocals.length;
int prevNumberOfLocals = prevFrame.getNumberOfLocals();
int currentNumberOfLocals = this.getNumberOfLocals();
int result = 0;
if (prevNumberOfLocals == 0) {
if (currentNumberOfLocals != 0) {
// need to check if there is a hole in the locals
result = currentNumberOfLocals; // append if no hole and currentNumberOfLocals <= 3
int counter = 0;
for(int i = 0; i < currentLocalsLength && counter < currentNumberOfLocals; i++) {
if (currentLocals[i] != null) {
switch(currentLocals[i].id()) {
case TypeIds.T_double :
case TypeIds.T_long :
i++;
}
counter++;
} else {
result = Integer.MAX_VALUE;
this.numberOfDifferentLocals = result;
return result;
}
}
}
} else if (currentNumberOfLocals == 0) {
// need to check if there is a hole in the prev locals
int counter = 0;
result = -prevNumberOfLocals; // chop frame if no hole and prevNumberOfLocals <= 3
for(int i = 0; i < prevLocalsLength && counter < prevNumberOfLocals; i++) {
if (prevLocals[i] != null) {
switch(prevLocals[i].id()) {
case TypeIds.T_double :
case TypeIds.T_long :
i++;
}
counter++;
} else {
result = Integer.MAX_VALUE;
this.numberOfDifferentLocals = result;
return result;
}
}
} else {
// need to see if prevLocals matches with currentLocals
int indexInPrevLocals = 0;
int indexInCurrentLocals = 0;
int currentLocalsCounter = 0;
int prevLocalsCounter = 0;
currentLocalsLoop: for (;indexInCurrentLocals < currentLocalsLength && currentLocalsCounter < currentNumberOfLocals; indexInCurrentLocals++) {
VerificationTypeInfo currentLocal = currentLocals[indexInCurrentLocals];
if (currentLocal != null) {
currentLocalsCounter++;
switch(currentLocal.id()) {
case TypeIds.T_double :
case TypeIds.T_long :
indexInCurrentLocals++; // next entry is null
}
}
for (;indexInPrevLocals < prevLocalsLength && prevLocalsCounter < prevNumberOfLocals; indexInPrevLocals++) {
VerificationTypeInfo prevLocal = prevLocals[indexInPrevLocals];
if (prevLocal != null) {
prevLocalsCounter++;
switch(prevLocal.id()) {
case TypeIds.T_double :
case TypeIds.T_long :
indexInPrevLocals++; // next entry is null
}
}
// now we need to check if prevLocal matches with currentLocal
// the index must be the same
if (equals(prevLocal, currentLocal) && indexInPrevLocals == indexInCurrentLocals) {
if (result != 0) {
result = Integer.MAX_VALUE;
this.numberOfDifferentLocals = result;
return result;
}
} else {
// locals at the same location are not equals - this has to be a full frame
result = Integer.MAX_VALUE;
this.numberOfDifferentLocals = result;
return result;
}
indexInPrevLocals++;
continue currentLocalsLoop;
}
// process remaining current locals
if (currentLocal != null) {
result++;
} else {
result = Integer.MAX_VALUE;
this.numberOfDifferentLocals = result;
return result;
}
indexInCurrentLocals++;
break currentLocalsLoop;
}
if (currentLocalsCounter < currentNumberOfLocals) {
for(;indexInCurrentLocals < currentLocalsLength && currentLocalsCounter < currentNumberOfLocals; indexInCurrentLocals++) {
VerificationTypeInfo currentLocal = currentLocals[indexInCurrentLocals];
if (currentLocal == null) {
result = Integer.MAX_VALUE;
this.numberOfDifferentLocals = result;
return result;
}
result++;
currentLocalsCounter++;
switch(currentLocal.id()) {
case TypeIds.T_double :
case TypeIds.T_long :
indexInCurrentLocals++; // next entry is null
}
}
} else if (prevLocalsCounter < prevNumberOfLocals) {
result = -result;
// process possible remaining prev locals
for(; indexInPrevLocals < prevLocalsLength && prevLocalsCounter < prevNumberOfLocals; indexInPrevLocals++) {
VerificationTypeInfo prevLocal = prevLocals[indexInPrevLocals];
if (prevLocal == null) {
result = Integer.MAX_VALUE;
this.numberOfDifferentLocals = result;
return result;
}
result--;
prevLocalsCounter++;
switch(prevLocal.id()) {
case TypeIds.T_double :
case TypeIds.T_long :
indexInPrevLocals++; // next entry is null
}
}
}
}
this.numberOfDifferentLocals = result;
return result;
}
public int getNumberOfLocals() {
if (this.numberOfLocals != -1) {
return this.numberOfLocals;
}
int result = 0;
final int length = this.locals == null ? 0 : this.locals.length;
for(int i = 0; i < length; i++) {
if (this.locals[i] != null) {
switch(this.locals[i].id()) {
case TypeIds.T_double :
case TypeIds.T_long :
i++;
}
result++;
}
}
this.numberOfLocals = result;
return result;
}
public int getOffsetDelta(StackMapFrame prevFrame) {
if (prevFrame == null) return this.pc;
return prevFrame.pc == -1 ? this.pc : this.pc - prevFrame.pc - 1;
}
public String toString() {
StringBuffer buffer = new StringBuffer();
printFrame(buffer, this);
return String.valueOf(buffer);
}
private void printFrame(StringBuffer buffer, StackMapFrame frame) {
String pattern = "[pc : {0} locals: {1} stack items: {2}\n{3}\n{4}\n]"; //$NON-NLS-1$
int localsLength = frame.locals == null ? 0 : frame.locals.length;
buffer.append(MessageFormat.format(
pattern,
new String[] {
Integer.toString(frame.pc),
Integer.toString(frame.getNumberOfLocals()),
Integer.toString(frame.numberOfStackItems),
print(frame.locals, localsLength),
print(frame.stackItems, frame.numberOfStackItems)
}
));
}
private String print(VerificationTypeInfo[] infos, int length) {
StringBuffer buffer = new StringBuffer();
buffer.append('[');
if (infos != null) {
for (int i = 0; i < length; i++) {
if (i != 0) buffer.append(',');
if (infos[i] == null) {
buffer.append("top"); //$NON-NLS-1$
continue;
}
switch(infos[i].tag) {
case VerificationTypeInfo.ITEM_NULL :
buffer.append("null"); //$NON-NLS-1$
break;
case VerificationTypeInfo.ITEM_UNINITIALIZED_THIS :
buffer.append("uninitialized_this"); //$NON-NLS-1$
break;
case VerificationTypeInfo.ITEM_TOP :
buffer.append("top"); //$NON-NLS-1$
break;
case VerificationTypeInfo.ITEM_UNINITIALIZED :
buffer.append("uninitialized(").append(infos[i].readableName()).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
break;
default:
buffer.append(infos[i].readableName());
}
}
}
buffer.append(']');
return String.valueOf(buffer);
}
public void setTopOfStack(TypeBinding typeBinding) {
this.stackItems[this.numberOfStackItems - 1].setBinding(typeBinding);
}
public void initializeReceiver() {
if (this.numberOfStackItems > 0) {
this.stackItems[this.numberOfStackItems - 1].tag = VerificationTypeInfo.ITEM_OBJECT;
}
}
public void removeLocals(int resolvedPosition) {
if (this.locals == null || resolvedPosition < 0) return;
if (resolvedPosition < this.locals.length) {
this.locals[resolvedPosition] = null;
}
}
public void putLocal(int resolvedPosition, VerificationTypeInfo info) {
if (this.locals == null) {
this.locals = new VerificationTypeInfo[resolvedPosition + 1];
this.locals[resolvedPosition] = info;
} else {
final int length = this.locals.length;
if (resolvedPosition >= length) {
System.arraycopy(this.locals, 0, this.locals = new VerificationTypeInfo[resolvedPosition + 1], 0, length);
}
this.locals[resolvedPosition] = info;
}
}
public void replaceWithElementType() {
VerificationTypeInfo info = this.stackItems[this.numberOfStackItems - 1];
try {
VerificationTypeInfo info2 = (VerificationTypeInfo) info.clone();
info2.replaceWithElementType();
this.stackItems[this.numberOfStackItems - 1] = info2;
} catch (CloneNotSupportedException e) {
// ignore
}
}
public int getIndexOfDifferentLocals(int differentLocalsCount) {
for (int i = this.locals.length - 1; i >= 0; i--) {
VerificationTypeInfo currentLocal = this.locals[i];
if (currentLocal == null) {
// check the previous slot
continue;
} else {
differentLocalsCount--;
}
if (differentLocalsCount == 0) {
return i;
}
}
return 0;
}
private boolean equals(VerificationTypeInfo info, VerificationTypeInfo info2) {
if (info == null) {
return info2 == null;
}
if (info2 == null) return false;
return info.equals(info2);
}
public void mergeLocals(StackMapFrame currentFrame) {
int currentFrameLocalsLength = currentFrame.locals == null ? 0 : currentFrame.locals.length;
int localsLength = this.locals == null ? 0 : this.locals.length;
for (int i = 0, max = Math.min(currentFrameLocalsLength, localsLength); i < max; i++) {
VerificationTypeInfo info = this.locals[i];
VerificationTypeInfo info2 = currentFrame.locals[i];
if (info == null) {
if (info2 != null) {
this.locals[i] = info2;
}
} else if (info2 == null) {
this.locals[i] = null;
} else {
int tag1 = info.tag;
int tag2 = info2.tag;
if (tag1 != tag2) {
this.locals[i] = null;
}
}
}
}
}