org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2017 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
 *      Stephan Herrmann - Contribution for
 *                        bug 400710 - [1.8][compiler] synthetic access to default method generates wrong code
 *                        Bug 459967 - [null] compiler should know about nullness of special methods like MyEnum.valueOf()
 *                        Bug 470467 - [null] Nullness of special Enum methods not detected from .class file
 *      Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
 *                             Bug 405104 - [1.8][compiler][codegen] Implement support for serializeable lambdas
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import java.util.stream.Stream;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;

public class SyntheticMethodBinding extends MethodBinding {

    public FieldBinding targetReadField; // read access to a field
    public FieldBinding targetWriteField; // write access to a field
    public MethodBinding targetMethod; // method or constructor
    public TypeBinding targetEnumType; // enum type
    public LambdaExpression lambda;

    /** Switch (one from many) linked to the switch table */
    public SwitchStatement switchStatement;
    /**
     * Method reference expression whose target FI is Serializable. Should be set when
     * purpose is {@link #SerializableMethodReference}
     */
    public ReferenceExpression serializableMethodRef;
    public int purpose;

    // fields used to generate enum constants when too many
    public int startIndex;
    public int endIndex;

    public final static int FieldReadAccess = 1; // field read
    public final static int FieldWriteAccess = 2; // field write
    public final static int SuperFieldReadAccess = 3; // super field read
    public final static int SuperFieldWriteAccess = 4; // super field write
    public final static int MethodAccess = 5; // normal method
    public final static int ConstructorAccess = 6; // constructor
    public final static int SuperMethodAccess = 7; // super method
    public final static int BridgeMethod = 8; // bridge method
    public final static int EnumValues = 9; // enum #values()
    public final static int EnumValueOf = 10; // enum #valueOf(String)
    public final static int SwitchTable = 11; // switch table method
    public final static int TooManyEnumsConstants = 12; // too many enum constants
    public static final int LambdaMethod = 13; // Lambda body emitted as a method.
    public final static int ArrayConstructor = 14; // X[]::new
    public static final int ArrayClone = 15; // X[]::clone
    public static final int FactoryMethod = 16; // for indy call to private constructor.
    public static final int DeserializeLambda = 17; // For supporting lambda deserialization.
    /**
     * Serves as a placeholder for a method reference whose target FI is Serializable.
     * Is never directly materialized in bytecode
     */
    public static final int SerializableMethodReference = 18;

    public int sourceStart = 0; // start position of the matching declaration
    public int index; // used for sorting access methods in the class file
    public int fakePaddedParameters = 0; // added in synthetic constructor to avoid name clash.

    public SyntheticMethodBinding(FieldBinding targetField, boolean isReadAccess, boolean isSuperAccess,
            ReferenceBinding declaringClass) {

        this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccStatic
                | ClassFileConstants.AccSynthetic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
        SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
        this.selector = CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX,
                String.valueOf(methodId).toCharArray());
        if (isReadAccess) {
            this.returnType = targetField.type;
            if (targetField.isStatic()) {
                this.parameters = Binding.NO_PARAMETERS;
            } else {
                this.parameters = new TypeBinding[1];
                this.parameters[0] = declaringSourceType;
            }
            this.targetReadField = targetField;
            this.purpose = isSuperAccess ? SyntheticMethodBinding.SuperFieldReadAccess
                    : SyntheticMethodBinding.FieldReadAccess;
        } else {
            this.returnType = TypeBinding.VOID;
            if (targetField.isStatic()) {
                this.parameters = new TypeBinding[1];
                this.parameters[0] = targetField.type;
            } else {
                this.parameters = new TypeBinding[2];
                this.parameters[0] = declaringSourceType;
                this.parameters[1] = targetField.type;
            }
            this.targetWriteField = targetField;
            this.purpose = isSuperAccess ? SyntheticMethodBinding.SuperFieldWriteAccess
                    : SyntheticMethodBinding.FieldWriteAccess;
        }
        this.thrownExceptions = Binding.NO_EXCEPTIONS;
        this.declaringClass = declaringSourceType;

        // check for method collision
        boolean needRename;
        do {
            check: {
                needRename = false;
                // check for collision with known methods
                long range;
                MethodBinding[] methods = declaringSourceType.methods();
                if ((range = ReferenceBinding.binarySearch(this.selector, methods)) >= 0) {
                    int paramCount = this.parameters.length;
                    nextMethod: for (int imethod = (int) range, end = (int) (range >> 32); imethod <= end; imethod++) {
                        MethodBinding method = methods[imethod];
                        if (method.parameters.length == paramCount) {
                            TypeBinding[] toMatch = method.parameters;
                            for (int i = 0; i < paramCount; i++) {
                                if (TypeBinding.notEquals(toMatch[i], this.parameters[i])) {
                                    continue nextMethod;
                                }
                            }
                            needRename = true;
                            break check;
                        }
                    }
                }
                // check for collision with synthetic accessors
                if (knownAccessMethods != null) {
                    for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
                        if (knownAccessMethods[i] == null)
                            continue;
                        if (CharOperation.equals(this.selector, knownAccessMethods[i].selector)
                                && areParametersEqual(methods[i])) {
                            needRename = true;
                            break check;
                        }
                    }
                }
            }
            if (needRename) { // retry with a selector postfixed by a growing methodId
                setSelector(CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX,
                        String.valueOf(++methodId).toCharArray()));
            }
        } while (needRename);

        // retrieve sourceStart position for the target field for line number attributes
        FieldDeclaration[] fieldDecls = declaringSourceType.scope.referenceContext.fields;
        if (fieldDecls != null) {
            for (int i = 0, max = fieldDecls.length; i < max; i++) {
                if (fieldDecls[i].binding == targetField) {
                    this.sourceStart = fieldDecls[i].sourceStart;
                    return;
                }
            }
        }

        /* did not find the target field declaration - it is a synthetic one
           public class A {
              public class B {
        public class C {
           void foo() {
              System.out.println("A.this = " + A.this);
           }
        }
              }
              public static void main(String args[]) {
        new A().new B().new C().foo();
              }
           }
        */
        // We now at this point - per construction - it is for sure an enclosing instance, we are going to
        // show the target field type declaration location.
        this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead
    }

    public SyntheticMethodBinding(FieldBinding targetField, ReferenceBinding declaringClass,
            TypeBinding enumBinding, char[] selector, SwitchStatement switchStatement) {
        this.modifiers = (declaringClass.isInterface() ? ClassFileConstants.AccPublic
                : ClassFileConstants.AccDefault) | ClassFileConstants.AccStatic | ClassFileConstants.AccSynthetic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
        SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
        this.selector = selector;
        this.returnType = declaringSourceType.scope.createArrayType(TypeBinding.INT, 1);
        this.parameters = Binding.NO_PARAMETERS;
        this.targetReadField = targetField;
        this.targetEnumType = enumBinding;
        this.purpose = SyntheticMethodBinding.SwitchTable;
        this.thrownExceptions = Binding.NO_EXCEPTIONS;
        this.declaringClass = declaringSourceType;
        this.switchStatement = switchStatement;
        if (declaringSourceType.isStrictfp()) {
            this.modifiers |= ClassFileConstants.AccStrictfp;
        }
        // check for method collision
        boolean needRename;
        do {
            check: {
                needRename = false;
                // check for collision with known methods
                long range;
                MethodBinding[] methods = declaringSourceType.methods();
                if ((range = ReferenceBinding.binarySearch(this.selector, methods)) >= 0) {
                    int paramCount = this.parameters.length;
                    nextMethod: for (int imethod = (int) range, end = (int) (range >> 32); imethod <= end; imethod++) {
                        MethodBinding method = methods[imethod];
                        if (method.parameters.length == paramCount) {
                            TypeBinding[] toMatch = method.parameters;
                            for (int i = 0; i < paramCount; i++) {
                                if (TypeBinding.notEquals(toMatch[i], this.parameters[i])) {
                                    continue nextMethod;
                                }
                            }
                            needRename = true;
                            break check;
                        }
                    }
                }
                // check for collision with synthetic accessors
                if (knownAccessMethods != null) {
                    for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
                        if (knownAccessMethods[i] == null)
                            continue;
                        if (CharOperation.equals(this.selector, knownAccessMethods[i].selector)
                                && areParametersEqual(methods[i])) {
                            needRename = true;
                            break check;
                        }
                    }
                }
            }
            if (needRename) { // retry with a selector postfixed by a growing methodId
                setSelector(CharOperation.concat(selector, String.valueOf(++methodId).toCharArray()));
            }
        } while (needRename);

        // We now at this point - per construction - it is for sure an enclosing instance, we are going to
        // show the target field type declaration location.
        this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead
    }

    public SyntheticMethodBinding(MethodBinding targetMethod, boolean isSuperAccess,
            ReferenceBinding declaringClass) {

        if (targetMethod.isConstructor()) {
            initializeConstructorAccessor(targetMethod);
        } else {
            initializeMethodAccessor(targetMethod, isSuperAccess, declaringClass);
        }
    }

    /**
     * Construct a bridge method
     */
    public SyntheticMethodBinding(MethodBinding overridenMethodToBridge, MethodBinding targetMethod,
            SourceTypeBinding declaringClass) {

        this.declaringClass = declaringClass;
        this.selector = overridenMethodToBridge.selector;
        // amongst other, clear the AccGenericSignature, so as to ensure no remains of original inherited persist (101794)
        // also use the modifiers from the target method, as opposed to inherited one (147690)
        this.modifiers = (targetMethod.modifiers | ClassFileConstants.AccBridge | ClassFileConstants.AccSynthetic)
                & ~(ClassFileConstants.AccSynchronized | ClassFileConstants.AccAbstract
                        | ClassFileConstants.AccNative | ClassFileConstants.AccFinal
                        | ExtraCompilerModifiers.AccGenericSignature);
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        this.returnType = overridenMethodToBridge.returnType;
        this.parameters = overridenMethodToBridge.parameters;
        this.thrownExceptions = overridenMethodToBridge.thrownExceptions;
        this.targetMethod = targetMethod;
        this.purpose = SyntheticMethodBinding.BridgeMethod;
        SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
    }

    /**
     * Construct enum special methods: values or valueOf methods
     */
    public SyntheticMethodBinding(SourceTypeBinding declaringEnum, char[] selector) {
        this.declaringClass = declaringEnum;
        this.selector = selector;
        this.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        LookupEnvironment environment = declaringEnum.scope.environment();
        this.thrownExceptions = Binding.NO_EXCEPTIONS;
        if (selector == TypeConstants.VALUES) {
            this.returnType = environment.createArrayType(environment.convertToParameterizedType(declaringEnum), 1);
            this.parameters = Binding.NO_PARAMETERS;
            this.purpose = SyntheticMethodBinding.EnumValues;
        } else if (selector == TypeConstants.VALUEOF) {
            this.returnType = environment.convertToParameterizedType(declaringEnum);
            this.parameters = new TypeBinding[] { declaringEnum.scope.getJavaLangString() };
            this.purpose = SyntheticMethodBinding.EnumValueOf;
        }
        SyntheticMethodBinding[] knownAccessMethods = ((SourceTypeBinding) this.declaringClass).syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
        if (declaringEnum.isStrictfp()) {
            this.modifiers |= ClassFileConstants.AccStrictfp;
        }
    }

    /**
     * Construct $deserializeLambda$ method
     */
    public SyntheticMethodBinding(SourceTypeBinding declaringClass) {
        this.declaringClass = declaringClass;
        this.selector = TypeConstants.DESERIALIZE_LAMBDA;
        this.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic
                | ClassFileConstants.AccSynthetic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        this.thrownExceptions = Binding.NO_EXCEPTIONS;
        this.returnType = declaringClass.scope.getJavaLangObject();
        this.parameters = new TypeBinding[] { declaringClass.scope.getJavaLangInvokeSerializedLambda() };
        this.purpose = SyntheticMethodBinding.DeserializeLambda;
        SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
    }

    /**
     * Construct enum special methods: values or valueOf methods
     */
    public SyntheticMethodBinding(SourceTypeBinding declaringEnum, int startIndex, int endIndex) {
        this.declaringClass = declaringEnum;
        SyntheticMethodBinding[] knownAccessMethods = declaringEnum.syntheticMethods();
        this.index = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        StringBuffer buffer = new StringBuffer();
        buffer.append(TypeConstants.SYNTHETIC_ENUM_CONSTANT_INITIALIZATION_METHOD_PREFIX).append(this.index);
        this.selector = String.valueOf(buffer).toCharArray();
        this.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccStatic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        this.purpose = SyntheticMethodBinding.TooManyEnumsConstants;
        this.thrownExceptions = Binding.NO_EXCEPTIONS;
        this.returnType = TypeBinding.VOID;
        this.parameters = Binding.NO_PARAMETERS;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }

    // Create a synthetic method that will simply call the super classes method.
    // Used when a public method is inherited from a non-public class into a public class.
    // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=288658
    // Also applies for inherited default methods with the same visibility issue.
    // See https://bugs.eclipse.org/400710
    public SyntheticMethodBinding(MethodBinding overridenMethodToBridge, SourceTypeBinding declaringClass) {

        this.declaringClass = declaringClass;
        this.selector = overridenMethodToBridge.selector;
        // amongst other, clear the AccGenericSignature, so as to ensure no remains of original inherited persist (101794)
        this.modifiers = (overridenMethodToBridge.modifiers | ClassFileConstants.AccBridge
                | ClassFileConstants.AccSynthetic)
                & ~(ClassFileConstants.AccSynchronized | ClassFileConstants.AccAbstract
                        | ClassFileConstants.AccNative | ClassFileConstants.AccFinal
                        | ExtraCompilerModifiers.AccGenericSignature);
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        this.returnType = overridenMethodToBridge.returnType;
        this.parameters = overridenMethodToBridge.parameters;
        this.thrownExceptions = overridenMethodToBridge.thrownExceptions;
        this.targetMethod = overridenMethodToBridge;
        this.purpose = SyntheticMethodBinding.SuperMethodAccess;
        SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
    }

    public SyntheticMethodBinding(int purpose, ArrayBinding arrayType, char[] selector,
            SourceTypeBinding declaringClass) {
        this.declaringClass = declaringClass;
        this.selector = selector;
        this.modifiers = ClassFileConstants.AccSynthetic | ClassFileConstants.AccPrivate
                | ClassFileConstants.AccStatic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        this.returnType = arrayType;
        LookupEnvironment environment = declaringClass.environment;
        if (environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
            // mark X[]::new and X[]::clone as returning 'X @NonNull' (don't wait (cf. markNonNull()), because we're called as late as codeGen):
            if (environment.usesNullTypeAnnotations())
                this.returnType = environment.createAnnotatedType(this.returnType,
                        new AnnotationBinding[] { environment.getNonNullAnnotation() });
            else
                this.tagBits |= TagBits.AnnotationNonNull;
        }
        this.parameters = new TypeBinding[] {
                purpose == SyntheticMethodBinding.ArrayConstructor ? TypeBinding.INT : (TypeBinding) arrayType };
        this.thrownExceptions = Binding.NO_EXCEPTIONS;
        this.purpose = purpose;
        SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
    }

    public SyntheticMethodBinding(LambdaExpression lambda, char[] lambdaName, SourceTypeBinding declaringClass) {
        this.lambda = lambda;
        this.declaringClass = declaringClass;
        this.selector = lambdaName;
        this.modifiers = lambda.binding.modifiers;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved)
                | (lambda.binding.tagBits & TagBits.HasParameterAnnotations);
        this.returnType = lambda.binding.returnType;
        this.parameters = lambda.binding.parameters;
        TypeVariableBinding[] vars = Stream.of(this.parameters).filter(param -> param.isTypeVariable())
                .toArray(TypeVariableBinding[]::new);
        if (vars != null && vars.length > 0)
            this.typeVariables = vars;
        this.thrownExceptions = lambda.binding.thrownExceptions;
        this.purpose = SyntheticMethodBinding.LambdaMethod;
        SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
    }

    public SyntheticMethodBinding(ReferenceExpression ref, SourceTypeBinding declaringClass) {
        this.serializableMethodRef = ref;
        this.declaringClass = declaringClass;
        this.selector = ref.binding.selector;
        this.modifiers = ref.binding.modifiers;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved)
                | (ref.binding.tagBits & TagBits.HasParameterAnnotations);
        this.returnType = ref.binding.returnType;
        this.parameters = ref.binding.parameters;
        this.thrownExceptions = ref.binding.thrownExceptions;
        this.purpose = SyntheticMethodBinding.SerializableMethodReference;
        SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
    }

    public SyntheticMethodBinding(MethodBinding privateConstructor, MethodBinding publicConstructor,
            char[] selector, TypeBinding[] enclosingInstances, SourceTypeBinding declaringClass) {
        this.declaringClass = declaringClass;
        this.selector = selector;
        this.modifiers = ClassFileConstants.AccSynthetic | ClassFileConstants.AccPrivate
                | ClassFileConstants.AccStatic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        this.returnType = publicConstructor.declaringClass;

        int realParametersLength = privateConstructor.parameters.length;
        int enclosingInstancesLength = enclosingInstances.length;
        int parametersLength = enclosingInstancesLength + realParametersLength;
        this.parameters = new TypeBinding[parametersLength];
        System.arraycopy(enclosingInstances, 0, this.parameters, 0, enclosingInstancesLength);
        System.arraycopy(privateConstructor.parameters, 0, this.parameters, enclosingInstancesLength,
                realParametersLength);
        this.fakePaddedParameters = publicConstructor.parameters.length - realParametersLength;

        this.thrownExceptions = publicConstructor.thrownExceptions;
        this.purpose = SyntheticMethodBinding.FactoryMethod;
        this.targetMethod = publicConstructor;
        SyntheticMethodBinding[] knownAccessMethods = declaringClass.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;
    }

    /**
     * An constructor accessor is a constructor with an extra argument (declaringClass), in case of
     * collision with an existing constructor, then add again an extra argument (declaringClass again).
     */
    public void initializeConstructorAccessor(MethodBinding accessedConstructor) {

        this.targetMethod = accessedConstructor;
        this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccSynthetic;
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        SourceTypeBinding sourceType = (SourceTypeBinding) accessedConstructor.declaringClass;
        SyntheticMethodBinding[] knownSyntheticMethods = sourceType.syntheticMethods();
        this.index = knownSyntheticMethods == null ? 0 : knownSyntheticMethods.length;

        this.selector = accessedConstructor.selector;
        this.returnType = accessedConstructor.returnType;
        this.purpose = SyntheticMethodBinding.ConstructorAccess;
        final int parametersLength = accessedConstructor.parameters.length;
        this.parameters = new TypeBinding[parametersLength + 1];
        System.arraycopy(accessedConstructor.parameters, 0, this.parameters, 0, parametersLength);
        this.parameters[parametersLength] = accessedConstructor.declaringClass;
        this.thrownExceptions = accessedConstructor.thrownExceptions;
        this.declaringClass = sourceType;

        // check for method collision
        boolean needRename;
        do {
            check: {
                needRename = false;
                // check for collision with known methods
                MethodBinding[] methods = sourceType.methods();
                for (int i = 0, length = methods.length; i < length; i++) {
                    if (CharOperation.equals(this.selector, methods[i].selector)
                            && areParameterErasuresEqual(methods[i])) {
                        needRename = true;
                        break check;
                    }
                }
                // check for collision with synthetic accessors
                if (knownSyntheticMethods != null) {
                    for (int i = 0, length = knownSyntheticMethods.length; i < length; i++) {
                        if (knownSyntheticMethods[i] == null)
                            continue;
                        if (CharOperation.equals(this.selector, knownSyntheticMethods[i].selector)
                                && areParameterErasuresEqual(knownSyntheticMethods[i])) {
                            needRename = true;
                            break check;
                        }
                    }
                }
            }
            if (needRename) { // retry with a new extra argument
                int length = this.parameters.length;
                System.arraycopy(this.parameters, 0, this.parameters = new TypeBinding[length + 1], 0, length);
                this.parameters[length] = this.declaringClass;
            }
        } while (needRename);

        // retrieve sourceStart position for the target method for line number attributes
        AbstractMethodDeclaration[] methodDecls = sourceType.scope.referenceContext.methods;
        if (methodDecls != null) {
            for (int i = 0, length = methodDecls.length; i < length; i++) {
                if (methodDecls[i].binding == accessedConstructor) {
                    this.sourceStart = methodDecls[i].sourceStart;
                    return;
                }
            }
        }
    }

    /**
     * An method accessor is a method with an access$N selector, where N is incremented in case of collisions.
     */
    public void initializeMethodAccessor(MethodBinding accessedMethod, boolean isSuperAccess,
            ReferenceBinding receiverType) {

        this.targetMethod = accessedMethod;
        if (isSuperAccess && receiverType.isInterface() && !accessedMethod.isStatic())
            this.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccSynthetic;
        else {
            if (receiverType.isInterface()) // default is not allowed. TODO: do we need a target level check here?
                this.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic
                        | ClassFileConstants.AccSynthetic;
            else
                this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccStatic
                        | ClassFileConstants.AccSynthetic;
        }
        this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
        SourceTypeBinding declaringSourceType = (SourceTypeBinding) receiverType;
        SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods();
        int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
        this.index = methodId;

        this.selector = CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX,
                String.valueOf(methodId).toCharArray());
        this.returnType = accessedMethod.returnType;
        this.purpose = isSuperAccess ? SyntheticMethodBinding.SuperMethodAccess
                : SyntheticMethodBinding.MethodAccess;

        if (accessedMethod.isStatic() || (isSuperAccess && receiverType.isInterface())) {
            this.parameters = accessedMethod.parameters;
        } else {
            this.parameters = new TypeBinding[accessedMethod.parameters.length + 1];
            this.parameters[0] = declaringSourceType;
            System.arraycopy(accessedMethod.parameters, 0, this.parameters, 1, accessedMethod.parameters.length);
        }
        this.thrownExceptions = accessedMethod.thrownExceptions;
        this.declaringClass = declaringSourceType;

        // check for method collision
        boolean needRename;
        do {
            check: {
                needRename = false;
                // check for collision with known methods
                MethodBinding[] methods = declaringSourceType.methods();
                for (int i = 0, length = methods.length; i < length; i++) {
                    if (CharOperation.equals(this.selector, methods[i].selector)
                            && areParameterErasuresEqual(methods[i])) {
                        needRename = true;
                        break check;
                    }
                }
                // check for collision with synthetic accessors
                if (knownAccessMethods != null) {
                    for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
                        if (knownAccessMethods[i] == null)
                            continue;
                        if (CharOperation.equals(this.selector, knownAccessMethods[i].selector)
                                && areParameterErasuresEqual(knownAccessMethods[i])) {
                            needRename = true;
                            break check;
                        }
                    }
                }
            }
            if (needRename) { // retry with a selector & a growing methodId
                setSelector(CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX,
                        String.valueOf(++methodId).toCharArray()));
            }
        } while (needRename);

        // retrieve sourceStart position for the target method for line number attributes
        AbstractMethodDeclaration[] methodDecls = declaringSourceType.scope.referenceContext.methods;
        if (methodDecls != null) {
            for (int i = 0, length = methodDecls.length; i < length; i++) {
                if (methodDecls[i].binding == accessedMethod) {
                    this.sourceStart = methodDecls[i].sourceStart;
                    return;
                }
            }
        }
    }

    protected boolean isConstructorRelated() {
        return this.purpose == SyntheticMethodBinding.ConstructorAccess;
    }

    @Override
    public LambdaExpression sourceLambda() {
        return this.lambda;
    }

    public void markNonNull(LookupEnvironment environment) {
        markNonNull(this, this.purpose, environment);
    }

    static void markNonNull(MethodBinding method, int purpose, LookupEnvironment environment) {
        // deferred update of the return type
        switch (purpose) {
        case EnumValues:
            if (environment.usesNullTypeAnnotations()) {
                TypeBinding elementType = ((ArrayBinding) method.returnType).leafComponentType();
                AnnotationBinding nonNullAnnotation = environment.getNonNullAnnotation();
                elementType = environment.createAnnotatedType(elementType,
                        new AnnotationBinding[] { environment.getNonNullAnnotation() });
                method.returnType = environment.createArrayType(elementType, 1,
                        new AnnotationBinding[] { nonNullAnnotation, null });
            } else {
                method.tagBits |= TagBits.AnnotationNonNull;
            }
            return;
        case EnumValueOf:
            if (environment.usesNullTypeAnnotations()) {
                method.returnType = environment.createAnnotatedType(method.returnType,
                        new AnnotationBinding[] { environment.getNonNullAnnotation() });
            } else {
                method.tagBits |= TagBits.AnnotationNonNull;
            }
            return;
        }
    }
}