/******************************************************************************* | |
* 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 Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.wst.jsdt.internal.compiler.codegen; | |
import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalVariableBinding; | |
import org.eclipse.wst.jsdt.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.wst.jsdt.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(); | |
} | |
} |