| /******************************************************************************* |
| * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; |
| import org.eclipse.jdt.internal.compiler.problem.AbortMethod; |
| |
| /** |
| * This type is a port of smalltalks JavaLabel |
| */ |
| public class Label { |
| public CodeStream codeStream; |
| final static int POS_NOT_SET = -1; |
| public int position = POS_NOT_SET; // position=POS_NOT_SET Then it's pos is not set. |
| public int[] forwardReferences = new int[10]; // Add an overflow check here. |
| public int forwardReferenceCount = 0; |
| private boolean isWide = false; |
| |
| public Label() { |
| // for creating labels ahead of code generation |
| } |
| /** |
| * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream |
| */ |
| public Label(CodeStream codeStream) { |
| this.codeStream = codeStream; |
| } |
| /** |
| * Add a forward refrence for the array. |
| */ |
| void addForwardReference(int iPos) { |
| int length; |
| if (forwardReferenceCount >= (length = forwardReferences.length)) |
| System.arraycopy(forwardReferences, 0, (forwardReferences = new int[2*length]), 0, length); |
| forwardReferences[forwardReferenceCount++] = iPos; |
| } |
| /** |
| * Add a forward refrence for the array. |
| */ |
| public void appendForwardReferencesFrom(Label otherLabel) { |
| int otherCount = otherLabel.forwardReferenceCount; |
| if (otherCount == 0) return; |
| int length = forwardReferences.length; |
| int neededSpace = otherCount + forwardReferenceCount; |
| if (neededSpace >= length){ |
| System.arraycopy(forwardReferences, 0, (forwardReferences = new int[neededSpace]), 0, forwardReferenceCount); |
| } |
| // append other forward references at the end, so they will get updated as well |
| System.arraycopy(otherLabel.forwardReferences, 0, forwardReferences, forwardReferenceCount, otherCount); |
| forwardReferenceCount = neededSpace; |
| } |
| /* |
| * Put down a reference to the array at the location in the codestream. |
| */ |
| void branch() { |
| if (position == POS_NOT_SET) { |
| addForwardReference(codeStream.position); |
| // Leave two bytes free to generate the jump afterwards |
| codeStream.position += 2; |
| codeStream.classFileOffset += 2; |
| } else { |
| /* |
| * Position is set. Write it if it is not a wide branch. |
| */ |
| int offset = position - codeStream.position + 1; |
| if (Math.abs(offset) > 0x7FFF && !this.codeStream.wideMode) { |
| throw new AbortMethod(CodeStream.RESTART_IN_WIDE_MODE, null); |
| } |
| codeStream.writeSignedShort(offset); |
| } |
| } |
| /* |
| * No support for wide branches yet |
| */ |
| void branchWide() { |
| if (position == POS_NOT_SET) { |
| addForwardReference(codeStream.position); |
| // Leave 4 bytes free to generate the jump offset afterwards |
| isWide = true; |
| codeStream.position += 4; |
| codeStream.classFileOffset += 4; |
| } else { //Position is set. Write it! |
| codeStream.writeSignedWord(position - codeStream.position + 1); |
| } |
| } |
| /** |
| * @return boolean |
| */ |
| public boolean hasForwardReferences() { |
| return forwardReferenceCount != 0; |
| } |
| /* |
| * Some placed labels might be branching to a goto bytecode which we can optimize better. |
| */ |
| public void inlineForwardReferencesFromLabelsTargeting(int gotoLocation) { |
| |
| /* |
| Code required to optimized unreachable gotos. |
| public boolean isBranchTarget(int location) { |
| Label[] labels = codeStream.labels; |
| for (int i = codeStream.countLabels - 1; i >= 0; i--){ |
| Label label = labels[i]; |
| if ((label.position == location) && label.isStandardLabel()){ |
| return true; |
| } |
| } |
| return false; |
| } |
| */ |
| |
| Label[] labels = codeStream.labels; |
| for (int i = codeStream.countLabels - 1; i >= 0; i--){ |
| Label label = labels[i]; |
| if ((label.position == gotoLocation) && label.isStandardLabel()){ |
| this.appendForwardReferencesFrom(label); |
| /* |
| Code required to optimized unreachable gotos. |
| label.position = POS_NOT_SET; |
| */ |
| } else { |
| break; // same target labels should be contiguous |
| } |
| } |
| } |
| public void initialize(CodeStream stream) { |
| this.codeStream = stream; |
| this.position = POS_NOT_SET; |
| this.forwardReferenceCount = 0; |
| } |
| public boolean isStandardLabel(){ |
| return true; |
| } |
| /* |
| * Place the label. If we have forward references resolve them. |
| */ |
| public void place() { // Currently lacking wide support. |
| if (CodeStream.DEBUG) System.out.println("\t\t\t\t<place at: "+codeStream.position+" - "+ this); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| if (position == POS_NOT_SET) { |
| position = codeStream.position; |
| codeStream.addLabel(this); |
| int oldPosition = position; |
| boolean isOptimizedBranch = false; |
| // TURNED OFF since fail on 1F4IRD9 |
| if (forwardReferenceCount != 0) { |
| isOptimizedBranch = (forwardReferences[forwardReferenceCount - 1] + 2 == position) && (codeStream.bCodeStream[codeStream.classFileOffset - 3] == Opcodes.OPC_goto); |
| if (isOptimizedBranch) { |
| codeStream.position = (position -= 3); |
| codeStream.classFileOffset -= 3; |
| forwardReferenceCount--; |
| // also update the PCs in the related debug attributes |
| /** OLD CODE |
| int index = codeStream.pcToSourceMapSize - 1; |
| while ((index >= 0) && (codeStream.pcToSourceMap[index][1] == oldPosition)) { |
| codeStream.pcToSourceMap[index--][1] = position; |
| } |
| */ |
| // Beginning of new code |
| int index = codeStream.pcToSourceMapSize - 2; |
| if (codeStream.lastEntryPC == oldPosition) { |
| codeStream.lastEntryPC = position; |
| } |
| if ((index >= 0) && (codeStream.pcToSourceMap[index] == position)) { |
| codeStream.pcToSourceMapSize-=2; |
| } |
| // end of new code |
| if (codeStream.generateLocalVariableTableAttributes) { |
| LocalVariableBinding locals[] = codeStream.locals; |
| for (int i = 0, max = locals.length; i < max; i++) { |
| LocalVariableBinding local = locals[i]; |
| if ((local != null) && (local.initializationCount > 0)) { |
| if (local.initializationPCs[((local.initializationCount - 1) << 1) + 1] == oldPosition) { |
| // we want to prevent interval of size 0 to have a negative size. |
| // see PR 1GIRQLA: ITPJCORE:ALL - ClassFormatError for local variable attribute |
| local.initializationPCs[((local.initializationCount - 1) << 1) + 1] = position; |
| } |
| if (local.initializationPCs[(local.initializationCount - 1) << 1] == oldPosition) { |
| local.initializationPCs[(local.initializationCount - 1) << 1] = position; |
| } |
| } |
| } |
| } |
| } |
| } |
| for (int i = 0; i < forwardReferenceCount; i++) { |
| int offset = position - forwardReferences[i] + 1; |
| if (Math.abs(offset) > 0x7FFF && !this.codeStream.wideMode) { |
| throw new AbortMethod(CodeStream.RESTART_IN_WIDE_MODE, null); |
| } |
| if (this.codeStream.wideMode) { |
| if (this.isWide) { |
| codeStream.writeSignedWord(forwardReferences[i], offset); |
| } else { |
| codeStream.writeSignedShort(forwardReferences[i], offset); |
| } |
| } else { |
| codeStream.writeSignedShort(forwardReferences[i], offset); |
| } |
| } |
| // For all labels placed at that position we check if we need to rewrite the jump |
| // offset. It is the case each time a label had a forward reference to the current position. |
| // Like we change the current position, we have to change the jump offset. See 1F4IRD9 for more details. |
| if (isOptimizedBranch) { |
| for (int i = 0; i < codeStream.countLabels; i++) { |
| Label label = codeStream.labels[i]; |
| if (oldPosition == label.position) { |
| label.position = position; |
| if (label instanceof CaseLabel) { |
| int offset = position - ((CaseLabel) label).instructionPosition; |
| for (int j = 0; j < label.forwardReferenceCount; j++) { |
| int forwardPosition = label.forwardReferences[j]; |
| codeStream.writeSignedWord(forwardPosition, offset); |
| } |
| } else { |
| for (int j = 0; j < label.forwardReferenceCount; j++) { |
| int forwardPosition = label.forwardReferences[j]; |
| int offset = position - forwardPosition + 1; |
| if (Math.abs(offset) > 0x7FFF && !this.codeStream.wideMode) { |
| throw new AbortMethod(CodeStream.RESTART_IN_WIDE_MODE, null); |
| } |
| if (this.codeStream.wideMode) { |
| if (this.isWide) { |
| codeStream.writeSignedWord(forwardPosition, offset); |
| } else { |
| codeStream.writeSignedShort(forwardPosition, offset); |
| } |
| } else { |
| codeStream.writeSignedShort(forwardPosition, offset); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| /** |
| * Print out the receiver |
| */ |
| public String toString() { |
| String basic = getClass().getName(); |
| basic = basic.substring(basic.lastIndexOf('.')+1); |
| StringBuffer buffer = new StringBuffer(basic); |
| buffer.append('@').append(Integer.toHexString(hashCode())); |
| buffer.append("(position=").append(position); //$NON-NLS-1$ |
| buffer.append(", forwards = ["); //$NON-NLS-1$ |
| for (int i = 0; i < forwardReferenceCount - 1; i++) |
| buffer.append(forwardReferences[i] + ", "); //$NON-NLS-1$ |
| if (forwardReferenceCount >= 1) |
| buffer.append(forwardReferences[forwardReferenceCount-1]); |
| buffer.append("] )"); //$NON-NLS-1$ |
| return buffer.toString(); |
| } |
| } |