/******************************************************************************* * Copyright (c) 2000, 2008 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.util.Arrays; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; public class BranchLabel extends Label { private int[] forwardReferences = new int[10]; // Add an overflow check here. private int forwardReferenceCount = 0; BranchLabel delegate; // // Label tagbits public int tagBits; public final static int WIDE = 1; public final static int USED = 2; public BranchLabel() { // for creating labels ahead of code generation } /** * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream */ public BranchLabel(CodeStream codeStream) { super(codeStream); } /** * Add a forward refrence for the array. */ void addForwardReference(int pos) { if (this.delegate != null) { this.delegate.addForwardReference(pos); return; } final int count = this.forwardReferenceCount; if (count >= 1) { int previousValue = this.forwardReferences[count - 1]; if (previousValue < pos) { int length; if (count >= (length = this.forwardReferences.length)) System.arraycopy(this.forwardReferences, 0, (this.forwardReferences = new int[2*length]), 0, length); this.forwardReferences[this.forwardReferenceCount++] = pos; } else if (previousValue > pos) { int[] refs = this.forwardReferences; // check for duplicates for (int i = 0, max = this.forwardReferenceCount; i < max; i++) { if (refs[i] == pos) return; // already recorded } int length; if (count >= (length = refs.length)) System.arraycopy(refs, 0, (this.forwardReferences = new int[2*length]), 0, length); this.forwardReferences[this.forwardReferenceCount++] = pos; Arrays.sort(this.forwardReferences, 0, this.forwardReferenceCount); } } else { int length; if (count >= (length = this.forwardReferences.length)) System.arraycopy(this.forwardReferences, 0, (this.forwardReferences = new int[2*length]), 0, length); this.forwardReferences[this.forwardReferenceCount++] = pos; } } /** * Makes the current label inline all references to the other label */ public void becomeDelegateFor(BranchLabel otherLabel) { // other label is delegating to receiver from now on otherLabel.delegate = this; // all existing forward refs to other label are inlined into current label final int otherCount = otherLabel.forwardReferenceCount; if (otherCount == 0) return; // need to merge the two sorted arrays of forward references int[] mergedForwardReferences = new int[this.forwardReferenceCount + otherCount]; int indexInMerge = 0; int j = 0; int i = 0; int max = this.forwardReferenceCount; int max2 = otherLabel.forwardReferenceCount; loop1 : for (; i < max; i++) { final int value1 = this.forwardReferences[i]; for (; j < max2; j++) { final int value2 = otherLabel.forwardReferences[j]; if (value1 < value2) { mergedForwardReferences[indexInMerge++] = value1; continue loop1; } else if (value1 == value2) { mergedForwardReferences[indexInMerge++] = value1; j++; continue loop1; } else { mergedForwardReferences[indexInMerge++] = value2; } } mergedForwardReferences[indexInMerge++] = value1; } for (; j < max2; j++) { mergedForwardReferences[indexInMerge++] = otherLabel.forwardReferences[j]; } this.forwardReferences = mergedForwardReferences; this.forwardReferenceCount = indexInMerge; } /* * Put down a reference to the array at the location in the codestream. */ void branch() { this.tagBits |= BranchLabel.USED; if (this.delegate != null) { this.delegate.branch(); return; } if (this.position == Label.POS_NOT_SET) { addForwardReference(this.codeStream.position); // Leave two bytes free to generate the jump afterwards this.codeStream.position += 2; this.codeStream.classFileOffset += 2; } else { /* * Position is set. Write it if it is not a wide branch. */ this.codeStream.writePosition(this); } } /* * No support for wide branches yet */ void branchWide() { this.tagBits |= BranchLabel.USED; if (this.delegate != null) { this.delegate.branchWide(); return; } if (this.position == Label.POS_NOT_SET) { addForwardReference(this.codeStream.position); // Leave 4 bytes free to generate the jump offset afterwards this.tagBits |= BranchLabel.WIDE; this.codeStream.position += 4; this.codeStream.classFileOffset += 4; } else { //Position is set. Write it! this.codeStream.writeWidePosition(this); } } public int forwardReferenceCount() { if (this.delegate != null) this.delegate.forwardReferenceCount(); return this.forwardReferenceCount; } public int[] forwardReferences() { if (this.delegate != null) this.delegate.forwardReferences(); return this.forwardReferences; } public void initialize(CodeStream stream) { this.codeStream = stream; this.position = Label.POS_NOT_SET; this.forwardReferenceCount = 0; this.delegate = null; } public boolean isCaseLabel() { return false; } public boolean isStandardLabel(){ return true; } /* * Place the label. If we have forward references resolve them. */ public void place() { // Currently lacking wide support. // if ((this.tagBits & USED) == 0 && this.forwardReferenceCount == 0) { // return; // } //TODO how can position be set already ? cannot place more than once if (this.position == Label.POS_NOT_SET) { this.position = this.codeStream.position; this.codeStream.addLabel(this); int oldPosition = this.position; boolean isOptimizedBranch = false; if (this.forwardReferenceCount != 0) { isOptimizedBranch = (this.forwardReferences[this.forwardReferenceCount - 1] + 2 == this.position) && (this.codeStream.bCodeStream[this.codeStream.classFileOffset - 3] == Opcodes.OPC_goto); if (isOptimizedBranch) { if (this.codeStream.lastAbruptCompletion == this.position) { this.codeStream.lastAbruptCompletion = -1; } this.codeStream.position = (this.position -= 3); this.codeStream.classFileOffset -= 3; this.forwardReferenceCount--; if (this.codeStream.lastEntryPC == oldPosition) { this.codeStream.lastEntryPC = this.position; } // end of new code if ((this.codeStream.generateAttributes & (ClassFileConstants.ATTR_VARS | ClassFileConstants.ATTR_STACK_MAP_TABLE | ClassFileConstants.ATTR_STACK_MAP)) != 0) { LocalVariableBinding locals[] = this.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] = this.position; } if (local.initializationPCs[(local.initializationCount - 1) << 1] == oldPosition) { local.initializationPCs[(local.initializationCount - 1) << 1] = this.position; } } } } if ((this.codeStream.generateAttributes & ClassFileConstants.ATTR_LINES) != 0) { // we need to remove all entries that is beyond this.position inside the pcToSourcerMap table this.codeStream.removeUnusedPcToSourceMapEntries(); } } } for (int i = 0; i < this.forwardReferenceCount; i++) { this.codeStream.writePosition(this, this.forwardReferences[i]); } // 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) { this.codeStream.optimizeBranch(oldPosition, this); } } } /** * 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(this.position); //$NON-NLS-1$ if (this.delegate != null) buffer.append("delegate=").append(this.delegate); //$NON-NLS-1$ buffer.append(", forwards = ["); //$NON-NLS-1$ for (int i = 0; i < this.forwardReferenceCount - 1; i++) buffer.append(this.forwardReferences[i] + ", "); //$NON-NLS-1$ if (this.forwardReferenceCount >= 1) buffer.append(this.forwardReferences[this.forwardReferenceCount-1]); buffer.append("] )"); //$NON-NLS-1$ return buffer.toString(); } }