| /******************************************************************************* |
| * Copyright (c) 2005, 2010 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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; |
| } |
| @Override |
| 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 Object[] { |
| 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); |
| } |
| } |