/******************************************************************************* * Copyright (c) 2005, 2010 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 { 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(int initialLocalSize) { this.locals = new VerificationTypeInfo[initialLocalSize]; this.numberOfLocals = -1; this.numberOfDifferentLocals = -1; } public int getFrameType(StackMapFrame prevFrame) { final int offsetDelta = getOffsetDelta(prevFrame); switch(this.numberOfStackItems) { case 0 : switch(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(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) { throw new IllegalArgumentException("info cannot be null"); //$NON-NLS-1$ } 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) { if (this.stackItems == null) { this.stackItems = new VerificationTypeInfo[1]; this.stackItems[0] = new VerificationTypeInfo(binding); 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++] = new VerificationTypeInfo(binding); } } public StackMapFrame duplicate() { int length = this.locals.length; StackMapFrame result = new StackMapFrame(length); result.numberOfLocals = -1; result.numberOfDifferentLocals = -1; result.pc = this.pc; result.numberOfStackItems = this.numberOfStackItems; 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.duplicate(); } } } length = this.numberOfStackItems; if (length != 0) { result.stackItems = new VerificationTypeInfo[length]; for (int i = 0; i < length; i++) { result.stackItems[i] = this.stackItems[i].duplicate(); } } return result; } 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 = 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 } } if (indexInPrevLocals < prevLocalsLength && prevLocalsCounter < prevNumberOfLocals) { 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}\nlocals: {3}\nstack: {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(','); VerificationTypeInfo verificationTypeInfo = infos[i]; if (verificationTypeInfo == null) { buffer.append("top"); //$NON-NLS-1$ continue; } buffer.append(verificationTypeInfo); } } buffer.append(']'); return String.valueOf(buffer); } 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]; VerificationTypeInfo info2 = info.duplicate(); info2.replaceWithElementType(); this.stackItems[this.numberOfStackItems - 1] = info2; } 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); } }