org.eclipse.jdt.internal.compiler.ast.FunctionalExpression.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ast.FunctionalExpression.java

Source

/*******************************************************************************
 * Copyright (c) 2013, 2019 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
 *     Jesper S Moller - Contributions for
 *                     bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression
 *                     Bug 405066 - [1.8][compiler][codegen] Implement code generation infrastructure for JSR335
 *     Stephan Herrmann - Contribution for
 *                     Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
 *                     Bug 423504 - [1.8] Implement "18.5.3 Functional Interface Parameterization Inference"
 *                     Bug 425142 - [1.8][compiler] NPE in ConstraintTypeFormula.reduceSubType
 *                     Bug 425153 - [1.8] Having wildcard allows incompatible types in a lambda expression
 *                     Bug 425156 - [1.8] Lambda as an argument is flagged with incompatible error
 *                     Bug 424403 - [1.8][compiler] Generic method call with method reference argument fails to resolve properly.
 *                     Bug 427438 - [1.8][compiler] NPE at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
 *                     Bug 428352 - [1.8][compiler] Resolution errors don't always surface
 *                     Bug 446442 - [1.8] merge null annotations from super methods
 *     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.ast;

import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.VANILLA_CONTEXT;

import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBindingVisitor;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;

public abstract class FunctionalExpression extends Expression {

    protected TypeBinding expectedType;
    public MethodBinding descriptor;
    public MethodBinding binding; // Code generation binding. May include synthetics. See getMethodBinding()
    protected MethodBinding actualMethodBinding; // void of synthetics.
    boolean ignoreFurtherInvestigation;
    protected ExpressionContext expressionContext = VANILLA_CONTEXT;
    public CompilationResult compilationResult;
    public BlockScope enclosingScope;
    public int bootstrapMethodNumber = -1;
    public boolean shouldCaptureInstance = false; // Whether the expression needs access to instance data of enclosing type
    protected static IErrorHandlingPolicy silentErrorHandlingPolicy = DefaultErrorHandlingPolicies
            .ignoreAllProblems();
    private boolean hasReportedSamProblem = false;
    public boolean hasDescripterProblem;
    public boolean isSerializable;
    public int ordinal;

    public FunctionalExpression(CompilationResult compilationResult) {
        this.compilationResult = compilationResult;
    }

    public FunctionalExpression() {
        super();
    }

    @Override
    public boolean isBoxingCompatibleWith(TypeBinding targetType, Scope scope) {
        return false;
    }

    public void setCompilationResult(CompilationResult compilationResult) {
        this.compilationResult = compilationResult;
    }

    // Return the actual (non-code generation) method binding that is void of synthetics.
    public MethodBinding getMethodBinding() {
        return null;
    }

    @Override
    public void setExpectedType(TypeBinding expectedType) {
        this.expectedType = expectedType;
    }

    @Override
    public void setExpressionContext(ExpressionContext context) {
        this.expressionContext = context;
    }

    @Override
    public ExpressionContext getExpressionContext() {
        return this.expressionContext;
    }

    @Override
    public boolean isPolyExpression(MethodBinding candidate) {
        return true;
    }

    @Override
    public boolean isPolyExpression() {
        return true; // always as per introduction of part D, JSR 335
    }

    @Override
    public boolean isFunctionalType() {
        return true;
    }

    @Override
    public boolean isPertinentToApplicability(TypeBinding targetType, MethodBinding method) {
        if (targetType instanceof TypeVariableBinding) {
            TypeVariableBinding typeVariable = (TypeVariableBinding) targetType;
            if (method != null) { // when called from type inference
                if (typeVariable.declaringElement == method)
                    return false;
                if (method.isConstructor() && typeVariable.declaringElement == method.declaringClass)
                    return false;
            } else { // for internal calls
                if (typeVariable.declaringElement instanceof MethodBinding)
                    return false;
            }
        }
        return true;
    }

    @Override
    public TypeBinding invocationTargetType() {
        if (this.expectedType == null)
            return null;
        // when during inference this expression mimics as an invocationSite,
        // we simulate an *invocation* of this functional expression,
        // where the expected type of the expression is the return type of the sam:
        MethodBinding sam = this.expectedType.getSingleAbstractMethod(this.enclosingScope, true);
        if (sam != null && sam.problemId() != ProblemReasons.NoSuchSingleAbstractMethod) {
            if (sam.isConstructor())
                return sam.declaringClass;
            else
                return sam.returnType;
        }
        return null;
    }

    @Override
    public TypeBinding expectedType() {
        return this.expectedType;
    }

    public boolean argumentsTypeElided() {
        return true;
        /* only exception: lambda with explicit argument types. */ }

    // Notify the compilation unit that it contains some functional types, taking care not to add any transient copies. this is assumed not to be a copy
    public int recordFunctionalType(Scope scope) {
        while (scope != null) {
            switch (scope.kind) {
            case Scope.METHOD_SCOPE:
                ReferenceContext context = ((MethodScope) scope).referenceContext;
                if (context instanceof LambdaExpression) {
                    LambdaExpression expression = (LambdaExpression) context;
                    if (expression != expression.original) // fake universe.
                        return 0;
                }
                break;
            case Scope.COMPILATION_UNIT_SCOPE:
                CompilationUnitDeclaration unit = ((CompilationUnitScope) scope).referenceContext;
                return unit.record(this);
            }
            scope = scope.parent;
        }
        return 0; // not reached.
    }

    @Override
    public TypeBinding resolveType(BlockScope blockScope) {
        return resolveType(blockScope, false);
    }

    public TypeBinding resolveType(BlockScope blockScope, boolean skipKosherCheck) {
        this.constant = Constant.NotAConstant;
        this.enclosingScope = blockScope;
        MethodBinding sam = this.expectedType == null ? null
                : this.expectedType.getSingleAbstractMethod(blockScope, argumentsTypeElided());
        if (sam == null) {
            blockScope.problemReporter().targetTypeIsNotAFunctionalInterface(this);
            return null;
        }
        if (!sam.isValidBinding() && sam.problemId() != ProblemReasons.ContradictoryNullAnnotations) {
            return reportSamProblem(blockScope, sam);
        }

        this.descriptor = sam;
        if (skipKosherCheck || kosherDescriptor(blockScope, sam, true)) {
            if (this.expectedType instanceof IntersectionTypeBinding18) {
                ReferenceBinding[] intersectingTypes = ((IntersectionTypeBinding18) this.expectedType).intersectingTypes;
                for (int t = 0, max = intersectingTypes.length; t < max; t++) {
                    if (intersectingTypes[t].findSuperTypeOriginatingFrom(TypeIds.T_JavaIoSerializable,
                            false /*Serializable is not a class*/) != null) {
                        this.isSerializable = true;
                        break;
                    }
                }
            } else if (this.expectedType.findSuperTypeOriginatingFrom(TypeIds.T_JavaIoSerializable,
                    false /*Serializable is not a class*/) != null) {
                this.isSerializable = true;
            }
            LookupEnvironment environment = blockScope.environment();
            if (environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
                NullAnnotationMatching.checkForContradictions(sam, this, blockScope);
            }
            return this.resolvedType = this.expectedType;
        }

        return this.resolvedType = null;
    }

    protected TypeBinding reportSamProblem(BlockScope blockScope, MethodBinding sam) {
        if (this.hasReportedSamProblem)
            return null;
        switch (sam.problemId()) {
        case ProblemReasons.NoSuchSingleAbstractMethod:
            blockScope.problemReporter().targetTypeIsNotAFunctionalInterface(this);
            this.hasReportedSamProblem = true;
            break;
        case ProblemReasons.NotAWellFormedParameterizedType:
            blockScope.problemReporter().illFormedParameterizationOfFunctionalInterface(this);
            this.hasReportedSamProblem = true;
            break;
        }
        return null;
    }

    class VisibilityInspector extends TypeBindingVisitor {

        private Scope scope;
        private boolean shouldChatter;
        private boolean visible = true;
        private FunctionalExpression expression;

        public VisibilityInspector(FunctionalExpression expression, Scope scope, boolean shouldChatter) {
            this.scope = scope;
            this.shouldChatter = shouldChatter;
            this.expression = expression;
        }

        private void checkVisibility(ReferenceBinding referenceBinding) {
            if (!referenceBinding.canBeSeenBy(this.scope)) {
                this.visible = false;
                if (this.shouldChatter)
                    this.scope.problemReporter().descriptorHasInvisibleType(this.expression, referenceBinding);
            }
        }

        @Override
        public boolean visit(ReferenceBinding referenceBinding) {
            checkVisibility(referenceBinding);
            return true;
        }

        @Override
        public boolean visit(ParameterizedTypeBinding parameterizedTypeBinding) {
            checkVisibility(parameterizedTypeBinding);
            return true;
        }

        @Override
        public boolean visit(RawTypeBinding rawTypeBinding) {
            checkVisibility(rawTypeBinding);
            return true;
        }

        public boolean visible(TypeBinding type) {
            TypeBindingVisitor.visit(this, type);
            return this.visible;
        }

        public boolean visible(TypeBinding[] types) {
            TypeBindingVisitor.visit(this, types);
            return this.visible;
        }

    }

    public boolean kosherDescriptor(Scope scope, MethodBinding sam, boolean shouldChatter) {

        VisibilityInspector inspector = new VisibilityInspector(this, scope, shouldChatter);

        boolean status = true;
        if (!inspector.visible(sam.returnType))
            status = false;
        if (!inspector.visible(sam.parameters))
            status = false;
        if (!inspector.visible(sam.thrownExceptions))
            status = false;
        if (!inspector.visible(this.expectedType))
            status = false;
        this.hasDescripterProblem |= !status;
        return status;
    }

    public int nullStatus(FlowInfo flowInfo) {
        return FlowInfo.NON_NULL;
    }

    public int diagnosticsSourceEnd() {
        return this.sourceEnd;
    }

    public MethodBinding[] getRequiredBridges() {

        class BridgeCollector {

            MethodBinding[] bridges;
            MethodBinding method;
            char[] selector;
            LookupEnvironment environment;
            Scope scope;

            BridgeCollector(ReferenceBinding functionalType, MethodBinding method) {
                this.method = method;
                this.selector = method.selector;
                this.environment = FunctionalExpression.this.enclosingScope.environment();
                this.scope = FunctionalExpression.this.enclosingScope;
                collectBridges(new ReferenceBinding[] { functionalType });
            }

            void collectBridges(ReferenceBinding[] interfaces) {
                int length = interfaces == null ? 0 : interfaces.length;
                for (int i = 0; i < length; i++) {
                    ReferenceBinding superInterface = interfaces[i];
                    if (superInterface == null)
                        continue;
                    MethodBinding[] methods = superInterface.getMethods(this.selector);
                    for (int j = 0, count = methods == null ? 0 : methods.length; j < count; j++) {
                        MethodBinding inheritedMethod = methods[j];
                        if (inheritedMethod == null || this.method == inheritedMethod) // descriptor declaring class may not be same functional interface target type.
                            continue;
                        if (inheritedMethod.isStatic() || inheritedMethod.redeclaresPublicObjectMethod(this.scope))
                            continue;
                        inheritedMethod = MethodVerifier.computeSubstituteMethod(inheritedMethod, this.method,
                                this.environment);
                        if (inheritedMethod == null
                                || !MethodVerifier.isSubstituteParameterSubsignature(this.method, inheritedMethod,
                                        this.environment)
                                || !MethodVerifier.areReturnTypesCompatible(this.method, inheritedMethod,
                                        this.environment))
                            continue;
                        final MethodBinding originalInherited = inheritedMethod.original();
                        final MethodBinding originalOverride = this.method.original();
                        if (!originalOverride.areParameterErasuresEqual(originalInherited) || TypeBinding.notEquals(
                                originalOverride.returnType.erasure(), originalInherited.returnType.erasure()))
                            add(originalInherited);
                    }
                    collectBridges(superInterface.superInterfaces());
                }
            }

            void add(MethodBinding inheritedMethod) {
                if (this.bridges == null) {
                    this.bridges = new MethodBinding[] { inheritedMethod };
                    return;
                }
                int length = this.bridges.length;
                for (int i = 0; i < length; i++) {
                    if (this.bridges[i].areParameterErasuresEqual(inheritedMethod) && TypeBinding.equalsEquals(
                            this.bridges[i].returnType.erasure(), inheritedMethod.returnType.erasure()))
                        return;
                }
                System.arraycopy(this.bridges, 0, this.bridges = new MethodBinding[length + 1], 0, length);
                this.bridges[length] = inheritedMethod;
            }

            MethodBinding[] getBridges() {
                return this.bridges;
            }
        }

        ReferenceBinding functionalType;
        if (this.expectedType instanceof IntersectionTypeBinding18) {
            functionalType = (ReferenceBinding) ((IntersectionTypeBinding18) this.expectedType)
                    .getSAMType(this.enclosingScope);
        } else {
            functionalType = (ReferenceBinding) this.expectedType;
        }
        return new BridgeCollector(functionalType, this.descriptor).getBridges();
    }

    boolean requiresBridges() {
        return getRequiredBridges() != null;
    }

    public void cleanUp() {
        // to be overridden by sub-classes
    }
}