org.eclipse.jdt.internal.compiler.codegen.BranchLabel.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.codegen.BranchLabel.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2008 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.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.
    */
    @Override
    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
     */
    @Override
    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();
    }
}