Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.eclipse.jdt.internal.compiler.ast.LambdaExpression Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2012, 2024 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 382721 - [1.8][compiler] Effectively final variables needs special treatment
* Bug 416885 - [1.8][compiler]IncompatibleClassChange error (edit)
* Stephan Herrmann - Contribution for
* bug 401030 - [1.8][null] Null analysis support for lambda methods.
* Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Bug 392238 - [1.8][compiler][null] Detect semantically invalid null type annotations
* 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 424205 - [1.8] Cannot infer type for diamond type with lambda on method invocation
* Bug 425798 - [1.8][compiler] Another NPE in ConstraintTypeFormula.reduceSubType
* 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 426563 - [1.8] AIOOBE when method with error invoked with lambda expression as argument
* Bug 420525 - [1.8] [compiler] Incorrect error "The type Integer does not define sum(Object, Object) that is applicable here"
* Bug 427438 - [1.8][compiler] NPE at org.eclipse.jdt.internal.compiler.ast.ConditionalExpression.generateCode(ConditionalExpression.java:280)
* Bug 428294 - [1.8][compiler] Type mismatch: cannot convert from List to Collection
* Bug 428786 - [1.8][compiler] Inference needs to compute the "ground target type" when reducing a lambda compatibility constraint
* Bug 428980 - [1.8][null] simple expression as lambda body doesn't leverage null annotation on argument
* Bug 429430 - [1.8] Lambdas and method reference infer wrong exception type with generics (RuntimeException instead of IOException)
* Bug 432110 - [1.8][compiler] nested lambda type incorrectly inferred vs javac
* Bug 438458 - [1.8][null] clean up handling of null type annotations wrt type variables
* Bug 441693 - [1.8][null] Bogus warning for type argument annotated with @NonNull
* Bug 452788 - [1.8][compiler] Type not correctly inferred in lambda expression
* Bug 453483 - [compiler][null][loop] Improve null analysis for loops
* Bug 455723 - Nonnull argument not correctly inferred in loop
* Bug 463728 - [1.8][compiler][inference] Ternary operator in lambda derives wrong type
* Andy Clement (GoPivotal, Inc) [email protected] - 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.INVOCATION_CONTEXT;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
import org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext;
import org.eclipse.jdt.internal.compiler.flow.ExceptionInferenceFlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18;
import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
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.ParameterizedGenericMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.PolyTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Substitution;
import org.eclipse.jdt.internal.compiler.lookup.Substitution.NullSubstitution;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope.Substitutor;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.AbortMethod;
import org.eclipse.jdt.internal.compiler.problem.AbortType;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
@SuppressWarnings({"rawtypes", "unchecked"})
public class LambdaExpression extends FunctionalExpression implements IPolyExpression, ReferenceContext, ProblemSeverities {
public Argument [] arguments;
private TypeBinding [] argumentTypes;
public int arrowPosition;
public Statement body;
public boolean hasParentheses;
public MethodScope scope;
boolean voidCompatible = true;
boolean valueCompatible = false;
boolean returnsValue;
private final boolean requiresGenericSignature;
boolean returnsVoid;
public LambdaExpression original = this;
private boolean committed = false;
public SyntheticArgumentBinding[] outerLocalVariables = NO_SYNTHETIC_ARGUMENTS;
public Map mapSyntheticEnclosingTypes = new HashMap<>();
public boolean hasOuterClassMemberReference = false;
private int outerLocalVariablesSlotSize = 0;
private boolean assistNode = false;
private ReferenceBinding classType;
private Set thrownExceptions;
private static final SyntheticArgumentBinding [] NO_SYNTHETIC_ARGUMENTS = new SyntheticArgumentBinding[0];
private static final Block NO_BODY = new Block(0);
private HashMap copiesPerTargetType;
protected Expression [] resultExpressions = NO_EXPRESSIONS;
public InferenceContext18 inferenceContext; // when performing tentative resolve keep a back reference to the driving context
private Map localTypes; // support look-up of a local type from this lambda copy
public boolean argumentsTypeVar = false;
int firstLocalLocal; // analysis index of first local variable (if any) post parameter(s) in the lambda; ("local local" as opposed to "outer local")
public LambdaExpression(CompilationResult compilationResult, boolean assistNode, boolean requiresGenericSignature) {
super(compilationResult);
this.assistNode = assistNode;
this.requiresGenericSignature = requiresGenericSignature;
setArguments(NO_ARGUMENTS);
setBody(NO_BODY);
}
public LambdaExpression(CompilationResult compilationResult, boolean assistNode) {
this(compilationResult, assistNode, false);
}
public void setArguments(Argument [] arguments) {
this.arguments = arguments != null ? arguments : ASTNode.NO_ARGUMENTS;
this.argumentTypes = new TypeBinding[arguments != null ? arguments.length : 0];
}
public Argument [] arguments() {
return this.arguments;
}
public TypeBinding[] argumentTypes() {
return this.argumentTypes;
}
public void setBody(Statement body) {
this.body = body == null ? NO_BODY : body;
}
public Statement body() {
return this.body;
}
public Expression[] resultExpressions() {
return this.resultExpressions;
}
public void setArrowPosition(int arrowPosition) {
this.arrowPosition = arrowPosition;
}
public int arrowPosition() {
return this.arrowPosition;
}
protected FunctionalExpression original() {
return this.original;
}
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
if (this.shouldCaptureInstance) {
this.binding.modifiers &= ~ClassFileConstants.AccStatic;
} else {
this.binding.modifiers |= ClassFileConstants.AccStatic;
}
SourceTypeBinding sourceType = currentScope.enclosingSourceType();
boolean firstSpill = !(this.binding instanceof SyntheticMethodBinding);
this.binding = sourceType.addSyntheticMethod(this);
int pc = codeStream.position;
StringBuilder signature = new StringBuilder();
signature.append('(');
if (this.shouldCaptureInstance) {
codeStream.aload_0();
signature.append(sourceType.signature());
}
for (int i = 0, length = this.outerLocalVariables == null ? 0 : this.outerLocalVariables.length; i < length; i++) {
SyntheticArgumentBinding syntheticArgument = this.outerLocalVariables[i];
if (this.shouldCaptureInstance && firstSpill) { // finally block handling results in extra spills, avoid side effect.
syntheticArgument.resolvedPosition++;
}
signature.append(syntheticArgument.type.signature());
Object[] path;
Binding target;
if (syntheticArgument.actualOuterLocalVariable != null) {
LocalVariableBinding capturedOuterLocal = syntheticArgument.actualOuterLocalVariable;
path = currentScope.getEmulationPath(capturedOuterLocal);
target = capturedOuterLocal;
} else {
path = currentScope.getEmulationPath(
(ReferenceBinding) syntheticArgument.type,
false /*not only exact match (that is, allow compatible)*/,
false);
target = syntheticArgument.type;
}
codeStream.generateOuterAccess(path, this, target, currentScope);
}
signature.append(')');
if (this.expectedType instanceof IntersectionTypeBinding18) {
signature.append(((IntersectionTypeBinding18)this.expectedType).getSAMType(currentScope).signature());
} else {
signature.append(this.expectedType.signature());
}
int invokeDynamicNumber = codeStream.classFile.recordBootstrapMethod(this);
codeStream.invokeDynamic(invokeDynamicNumber, (this.shouldCaptureInstance ? 1 : 0) + this.outerLocalVariablesSlotSize, 1, this.descriptor.selector, signature.toString().toCharArray(),
this.resolvedType.id, this.resolvedType);
if (!valueRequired)
codeStream.pop();
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
@Override
public boolean kosherDescriptor(Scope currentScope, MethodBinding sam, boolean shouldChatter) {
if (sam.typeVariables != Binding.NO_TYPE_VARIABLES) {
if (shouldChatter)
currentScope.problemReporter().lambdaExpressionCannotImplementGenericMethod(this, sam);
return false;
}
return super.kosherDescriptor(currentScope, sam, shouldChatter);
}
public void resolveTypeWithBindings(LocalVariableBinding[] bindings, BlockScope blockScope, boolean skipKosherCheck) {
blockScope.include(bindings);
try {
this.resolveType(blockScope, skipKosherCheck);
} finally {
blockScope.exclude(bindings);
}
}
/* This code is arranged so that we can continue with as much analysis as possible while avoiding
* mine fields that would result in a slew of spurious messages. This method is a merger of:
* @see org.eclipse.jdt.internal.compiler.lookup.MethodScope.createMethod(AbstractMethodDeclaration)
* @see org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding.resolveTypesFor(MethodBinding)
* @see org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolve(ClassScope)
*/
@Override
public TypeBinding resolveType(BlockScope blockScope, boolean skipKosherCheck) {
boolean argumentsTypeElided = argumentsTypeElided();
int argumentsLength = this.arguments == null ? 0 : this.arguments.length;
if (this.constant != Constant.NotAConstant) {
this.constant = Constant.NotAConstant;
this.enclosingScope = blockScope;
if (this.original == this)
this.ordinal = recordFunctionalType(blockScope);
if (!argumentsTypeElided) {
for (int i = 0; i < argumentsLength; i++)
this.argumentTypes[i] = this.arguments[i].type.resolveType(blockScope, true /* check bounds*/);
}
if (this.expectedType == null && this.expressionContext == INVOCATION_CONTEXT) {
return new PolyTypeBinding(this);
}
}
MethodScope methodScope = blockScope.methodScope();
this.scope = new MethodScope(blockScope, this, methodScope.isStatic, methodScope.lastVisibleFieldID);
this.scope.isConstructorCall = methodScope.isConstructorCall;
super.resolveType(blockScope, skipKosherCheck); // compute & capture interface function descriptor.
final boolean haveDescriptor = this.descriptor != null;
if (!skipKosherCheck && (!haveDescriptor || this.descriptor.typeVariables != Binding.NO_TYPE_VARIABLES)) // already complained in kosher*
return this.resolvedType = null;
this.binding = new MethodBinding(ClassFileConstants.AccPrivate | ClassFileConstants.AccSynthetic | ExtraCompilerModifiers.AccUnresolved,
CharOperation.concat(TypeConstants.ANONYMOUS_METHOD, Integer.toString(this.ordinal).toCharArray()), // will be fixed up later.
haveDescriptor ? this.descriptor.returnType : TypeBinding.VOID,
Binding.NO_PARAMETERS, // for now.
haveDescriptor ? this.descriptor.thrownExceptions : Binding.NO_EXCEPTIONS,
blockScope.enclosingSourceType());
this.binding.typeVariables = Binding.NO_TYPE_VARIABLES;
MethodScope enm = this.scope.namedMethodScope();
MethodBinding enmb = enm == null ? null : enm.referenceMethodBinding();
if (enmb != null && enmb.isViewedAsDeprecated()) {
this.binding.modifiers |= ExtraCompilerModifiers.AccDeprecatedImplicitly;
this.binding.tagBits |= enmb.tagBits & TagBits.AnnotationTerminallyDeprecated;
}
boolean argumentsHaveErrors = false;
if (haveDescriptor) {
int parametersLength = this.descriptor.parameters.length;
if (parametersLength != argumentsLength) {
this.scope.problemReporter().lambdaSignatureMismatched(this);
if (argumentsTypeElided || this.original != this) // no interest in continuing to error check copy.
return this.resolvedType = null; // FUBAR, bail out ...
else {
this.resolvedType = null; // continue to type check.
argumentsHaveErrors = true;
}
}
}
TypeBinding[] newParameters = new TypeBinding[argumentsLength];
AnnotationBinding [][] parameterAnnotations = null;
for (int i = 0; i < argumentsLength; i++) {
Argument argument = this.arguments[i];
if (argument.isVarArgs()) {
if (i == argumentsLength - 1) {
this.binding.modifiers |= ClassFileConstants.AccVarargs;
} else {
this.scope.problemReporter().illegalVarargInLambda(argument);
argumentsHaveErrors = true;
}
}
TypeBinding argumentType;
final TypeBinding expectedParameterType = haveDescriptor && i < this.descriptor.parameters.length ? this.descriptor.parameters[i] : null;
argumentType = argumentsTypeElided ? expectedParameterType : this.argumentTypes[i];
if (argumentType == null) {
argumentsHaveErrors = true;
} else if (argumentType == TypeBinding.VOID) {
this.scope.problemReporter().argumentTypeCannotBeVoid(this, argument);
argumentsHaveErrors = true;
} else {
if (!argumentType.isValidBinding()) {
this.binding.tagBits |= TagBits.HasUnresolvedArguments;
}
if ((argumentType.tagBits & TagBits.HasMissingType) != 0) {
this.binding.tagBits |= TagBits.HasMissingType;
}
}
}
if (!argumentsTypeElided && !argumentsHaveErrors) {
ReferenceBinding groundType = null;
ReferenceBinding expectedSAMType = null;
if (this.expectedType instanceof IntersectionTypeBinding18)
expectedSAMType = (ReferenceBinding) ((IntersectionTypeBinding18) this.expectedType).getSAMType(blockScope);
else if (this.expectedType instanceof ReferenceBinding)
expectedSAMType = (ReferenceBinding) this.expectedType;
if (expectedSAMType != null)
groundType = findGroundTargetType(blockScope, this.expectedType, expectedSAMType, argumentsTypeElided);
if (groundType != null) {
this.descriptor = groundType.getSingleAbstractMethod(blockScope, true);
if (!this.descriptor.isValidBinding()) {
reportSamProblem(blockScope, this.descriptor);
} else {
if (groundType != expectedSAMType) { //$IDENTITY-COMPARISON$
if (!groundType.isCompatibleWith(expectedSAMType, this.scope)) { // the ground has shifted, are we still on firm grounds ?
blockScope.problemReporter().typeMismatchError(groundType, this.expectedType, this, null); // report deliberately against block scope so as not to blame the lambda.
return null;
}
}
this.resolvedType = groundType;
}
} else {
this.binding = new ProblemMethodBinding(TypeConstants.ANONYMOUS_METHOD, null, ProblemReasons.NotAWellFormedParameterizedType);
reportSamProblem(blockScope, this.binding);
return this.resolvedType = null;
}
}
boolean parametersHaveErrors = false;
boolean genericSignatureNeeded = this.requiresGenericSignature || blockScope.compilerOptions().generateGenericSignatureForLambdaExpressions;
TypeBinding[] expectedParameterTypes = new TypeBinding[argumentsLength];
for (int i = 0; i < argumentsLength; i++) {
Argument argument = this.arguments[i];
TypeBinding argumentType;
final TypeBinding expectedParameterType = haveDescriptor && i < this.descriptor.parameters.length ? this.descriptor.parameters[i] : null;
argumentType = argumentsTypeElided ? expectedParameterType : this.argumentTypes[i];
expectedParameterTypes[i] = expectedParameterType;
if (argumentType != null && argumentType != TypeBinding.VOID) {
if (haveDescriptor && expectedParameterType != null && argumentType.isValidBinding() && TypeBinding.notEquals(argumentType, expectedParameterType)) {
if (expectedParameterType.isProperType(true)) {
if (!isOnlyWildcardMismatch(expectedParameterType, argumentType)) {
this.scope.problemReporter().lambdaParameterTypeMismatched(argument, argument.type, expectedParameterType);
parametersHaveErrors = true; // continue to type check, but don't signal success
}
}
}
if (genericSignatureNeeded) {
TypeBinding leafType = argumentType.leafComponentType();
if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0)
this.binding.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
}
newParameters[i] = argument.bind(this.scope, argumentType, false);
if (argument.annotations != null) {
this.binding.tagBits |= TagBits.HasParameterAnnotations;
if (parameterAnnotations == null) {
parameterAnnotations = new AnnotationBinding[argumentsLength][];
for (int j = 0; j < i; j++) {
parameterAnnotations[j] = Binding.NO_ANNOTATIONS;
}
}
parameterAnnotations[i] = argument.binding.getAnnotations();
} else if (parameterAnnotations != null) {
parameterAnnotations[i] = Binding.NO_ANNOTATIONS;
}
}
}
if (this.argumentsTypeVar) {
for (int i = 0; i < argumentsLength; ++i) {
this.arguments[i].type.resolvedType = expectedParameterTypes[i];
}
}
// only assign parameters if no problems are found
if (!argumentsHaveErrors) {
this.binding.parameters = newParameters;
if (parameterAnnotations != null)
this.binding.setParameterAnnotations(parameterAnnotations);
}
if (!argumentsTypeElided && !argumentsHaveErrors && this.binding.isVarargs()) {
if (!this.binding.parameters[this.binding.parameters.length - 1].isReifiable()) {
this.scope.problemReporter().possibleHeapPollutionFromVararg(this.arguments[this.arguments.length - 1]);
}
}
ReferenceBinding [] exceptions = this.binding.thrownExceptions;
int exceptionsLength = exceptions.length;
for (int i = 0; i < exceptionsLength; i++) {
ReferenceBinding exception = exceptions[i];
if ((exception.tagBits & TagBits.HasMissingType) != 0) {
this.binding.tagBits |= TagBits.HasMissingType;
}
if (genericSignatureNeeded)
this.binding.modifiers |= (exception.modifiers & ExtraCompilerModifiers.AccGenericSignature);
}
TypeBinding returnType = this.binding.returnType;
if (returnType != null) {
if ((returnType.tagBits & TagBits.HasMissingType) != 0) {
this.binding.tagBits |= TagBits.HasMissingType;
}
if (genericSignatureNeeded) {
TypeBinding leafType = returnType.leafComponentType();
if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0)
this.binding.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
}
} // TODO (stephan): else? (can that happen?)
if (haveDescriptor && !argumentsHaveErrors && blockScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
if (!argumentsTypeElided) {
AbstractMethodDeclaration.createArgumentBindings(this.arguments, this.binding, this.scope); // includes validation
// no application of null-ness default, hence also no warning regarding redundant null annotation
mergeParameterNullAnnotations(blockScope);
}
this.binding.tagBits |= (this.descriptor.tagBits & TagBits.AnnotationNullMASK);
}
this.binding.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
this.firstLocalLocal = this.scope.outerMostMethodScope().analysisIndex;
if (this.body instanceof Expression && ((Expression) this.body).isTrulyExpression()) {
Expression expression = (Expression) this.body;
new ReturnStatement(expression, expression.sourceStart, expression.sourceEnd, true).resolve(this.scope); // :-) ;-)
if (expression.resolvedType == TypeBinding.VOID && !expression.statementExpression())
this.scope.problemReporter().invalidExpressionAsStatement(expression);
} else {
this.body.resolve(this.scope);
/* At this point, shape analysis is complete for ((see returnsExpression(...))
- a lambda with an expression body,
- a lambda with a block body in which we saw a return statement naked or otherwise.
*/
if (!this.returnsVoid && !this.returnsValue)
this.valueCompatible = this.body.doesNotCompleteNormally();
}
if ((this.binding.tagBits & TagBits.HasMissingType) != 0) {
this.scope.problemReporter().missingTypeInLambda(this, this.binding);
}
if (this.shouldCaptureInstance && this.scope.isConstructorCall) {
this.scope.problemReporter().fieldsOrThisBeforeConstructorInvocation(this);
}
// beyond this point ensure that all local type bindings are their final binding:
updateLocalTypes();
if (this.original == this) {
this.committed = true; // the original has been resolved
}
return (argumentsHaveErrors || parametersHaveErrors) ? null : this.resolvedType;
}
// check if the given types are parameterized types and if their type arguments
// differ only in a wildcard
// ? and ? extends Object
private boolean isOnlyWildcardMismatch(TypeBinding expected, TypeBinding argument) {
boolean onlyWildcardMismatch = false;
if (expected.isParameterizedType() && argument.isParameterizedType()) {
TypeBinding[] expectedArgs = ((ParameterizedTypeBinding)expected).typeArguments();
TypeBinding[] args = ((ParameterizedTypeBinding)argument).typeArguments();
if (args.length != expectedArgs.length)
return false;
for (int j = 0; j < args.length; j++) {
if (TypeBinding.notEquals(expectedArgs[j], args[j])) {
if (expectedArgs[j].isWildcard() && args[j].isUnboundWildcard()) {
WildcardBinding wc = (WildcardBinding)expectedArgs[j];
TypeBinding bound = wc.allBounds();
if (bound != null && wc.boundKind == Wildcard.EXTENDS && bound.id == TypeIds.T_JavaLangObject)
onlyWildcardMismatch = true;
} else {
onlyWildcardMismatch = false;
break;
}
}
}
}
return onlyWildcardMismatch;
}
private ReferenceBinding findGroundTargetType(BlockScope blockScope, TypeBinding targetType, TypeBinding expectedSAMType, boolean argumentTypesElided) {
if (expectedSAMType instanceof IntersectionTypeBinding18)
expectedSAMType = ((IntersectionTypeBinding18) expectedSAMType).getSAMType(blockScope);
if (expectedSAMType instanceof ReferenceBinding && expectedSAMType.isValidBinding()) {
ParameterizedTypeBinding withWildCards = InferenceContext18.parameterizedWithWildcard(expectedSAMType);
if (withWildCards != null) {
if (!argumentTypesElided) {
InferenceContext18 freshInferenceContext = new InferenceContext18(blockScope);
try {
return freshInferenceContext.inferFunctionalInterfaceParameterization(this, blockScope, withWildCards);
} finally {
freshInferenceContext.cleanUp();
}
} else {
return withWildCards.getNonWildcardParameterization(blockScope);
}
}
if (targetType instanceof ReferenceBinding)
return (ReferenceBinding) targetType;
}
return null;
}
@Override
public boolean argumentsTypeElided() {
return (this.arguments.length > 0 && this.arguments[0].hasElidedType()) || this.argumentsTypeVar;
}
private void analyzeExceptions() {
ExceptionHandlingFlowContext ehfc;
CompilerOptions compilerOptions = this.scope.compilerOptions();
boolean oldAnalyseResources = compilerOptions.analyseResourceLeaks;
compilerOptions.analyseResourceLeaks = false;
try {
this.body.analyseCode(this.scope,
ehfc = new ExceptionInferenceFlowContext(null, this, Binding.NO_EXCEPTIONS, null, this.scope, FlowInfo.DEAD_END),
UnconditionalFlowInfo.fakeInitializedFlowInfo(this.firstLocalLocal, this.scope.referenceType().maxFieldCount));
this.thrownExceptions = ehfc.extendedExceptions == null ? Collections.emptySet() : new HashSet(ehfc.extendedExceptions);
} catch (Exception e) {
// drop silently.
} finally {
compilerOptions.analyseResourceLeaks = oldAnalyseResources;
}
}
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, final FlowInfo flowInfo) {
if (this.ignoreFurtherInvestigation)
return flowInfo;
FlowInfo lambdaInfo = flowInfo.copy(); // what happens in vegas, stays in vegas ...
ExceptionHandlingFlowContext methodContext =
new ExceptionHandlingFlowContext(
flowContext,
this,
this.binding.thrownExceptions,
flowContext.getInitializationContext(),
this.scope,
FlowInfo.DEAD_END);
// nullity, owning and mark as assigned
MethodBinding methodWithParameterDeclaration = argumentsTypeElided() ? this.descriptor : this.binding;
AbstractMethodDeclaration.analyseArguments(currentScope.environment(), lambdaInfo, flowContext, this.arguments, methodWithParameterDeclaration);
if (this.arguments != null) {
for (Argument argument : this.arguments) {
this.bits |= (argument.bits & ASTNode.HasTypeAnnotations);
}
}
lambdaInfo = this.body.analyseCode(this.scope, methodContext, lambdaInfo);
// check for missing returning path for block body's ...
if (this.body instanceof Block) {
TypeBinding returnTypeBinding = expectedResultType();
if ((returnTypeBinding == TypeBinding.VOID)) {
if ((lambdaInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0 || ((Block) this.body).statements == null) {
this.bits |= ASTNode.NeedFreeReturn;
}
} else {
if (lambdaInfo != FlowInfo.DEAD_END) {
this.scope.problemReporter().shouldReturn(returnTypeBinding, this);
}
}
} else if (this.body instanceof Expression expression ){
if (lambdaInfo.reachMode() == FlowInfo.REACHABLE) {
CompilerOptions compilerOptions = currentScope.compilerOptions();
if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) {
checkAgainstNullAnnotation(flowContext, expression, flowInfo, expression.nullStatus(lambdaInfo, flowContext));
}
if (compilerOptions.analyseResourceLeaks) {
long owningTagBits = this.descriptor.tagBits & TagBits.AnnotationOwningMASK;
ReturnStatement.anylizeCloseableReturnExpression(expression, currentScope, owningTagBits, flowContext, flowInfo);
}
}
}
return flowInfo;
}
// cf. AbstractMethodDeclaration.validateNullAnnotations()
// pre: !argumentTypeElided()
void validateNullAnnotations() {
// null annotations on parameters?
if (this.binding != null) {
int length = this.binding.parameters.length;
for (int i=0; i> ASTNode.ParenthesizedSHIFT;
String suffix = ""; //$NON-NLS-1$
for(int i = 0; i < parenthesesCount; i++) {
output.append('(');
suffix += ')';
}
output.append('(');
if (this.arguments != null) {
for (int i = 0; i < this.arguments.length; i++) {
if (i > 0) output.append(", "); //$NON-NLS-1$
this.arguments[i].print(0, output);
}
}
output.append(") -> " ); //$NON-NLS-1$
if (makeShort) {
output.append("{}"); //$NON-NLS-1$
} else {
if (this.body != null)
this.body.print(this.body instanceof Block ? tab : 0, output);
else
output.append("<@incubator>"); //$NON-NLS-1$
}
return output.append(suffix);
}
public TypeBinding expectedResultType() {
return this.descriptor != null && this.descriptor.isValidBinding() ? this.descriptor.returnType : null;
}
@Override
public void traverse(ASTVisitor visitor, BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
if (this.arguments != null) {
int argumentsLength = this.arguments.length;
for (int i = 0; i < argumentsLength; i++)
this.arguments[i].traverse(visitor, this.scope);
}
if (this.body != null) {
this.body.traverse(visitor, this.scope);
}
}
visitor.endVisit(this, blockScope);
}
public MethodScope getScope() {
return this.scope;
}
private void analyzeShape() { // Simple minded analysis for code assist & potential compatibility.
class ShapeComputer extends ASTVisitor {
@Override
public boolean visit(TypeDeclaration type, BlockScope skope) {
return false;
}
@Override
public boolean visit(TypeDeclaration type, ClassScope skope) {
return false;
}
@Override
public boolean visit(LambdaExpression type, BlockScope skope) {
return false;
}
@Override
public boolean visit(ReturnStatement returnStatement, BlockScope skope) {
if (returnStatement.expression != null) {
LambdaExpression.this.valueCompatible = true;
LambdaExpression.this.voidCompatible = false;
LambdaExpression.this.returnsValue = true;
} else {
LambdaExpression.this.voidCompatible = true;
LambdaExpression.this.valueCompatible = false;
LambdaExpression.this.returnsVoid = true;
}
return false;
}
}
if (this.body instanceof Expression && ((Expression) this.body).isTrulyExpression()) {
// When completion is still in progress, it is not possible to ask if the expression constitutes a statement expression. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219
this.voidCompatible = this.assistNode ? true : ((Expression) this.body).statementExpression();
this.valueCompatible = true; // expression could be of type void - we can't determine that as we are working with unresolved expressions, for potential compatibility it is OK.
} else {
// For code assist, we need to be a bit tolerant/fuzzy here: the code is being written "just now", if we are too pedantic, selection/completion will break;
if (this.assistNode) {
this.voidCompatible = true;
this.valueCompatible = true;
}
this.body.traverse(new ShapeComputer(), null);
if (!this.returnsValue && !this.returnsVoid)
this.valueCompatible = this.body.doesNotCompleteNormally();
}
}
@Override
public boolean isPotentiallyCompatibleWith(TypeBinding targetType, Scope skope) {
/* We get here only when the lambda is NOT pertinent to applicability and that too only for type elided lambdas. */
/* 15.12.2.1: A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:
– The arity of the target type's function type is the same as the arity of the lambda expression.
– If the target type's function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).
– If the target type's function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).
*/
if (!super.isPertinentToApplicability(targetType, null))
return true;
final MethodBinding sam = targetType.getSingleAbstractMethod(skope, true);
if (sam == null || !sam.isValidBinding())
return false;
if (sam.parameters.length != this.arguments.length)
return false;
analyzeShape();
if (sam.returnType.id == TypeIds.T_void) {
if (!this.voidCompatible)
return false;
} else {
if (!this.valueCompatible)
return false;
}
return true;
}
private enum CompatibilityResult { COMPATIBLE, INCOMPATIBLE, REPORTED }
public boolean reportShapeError(TypeBinding targetType, Scope skope) {
return internalIsCompatibleWith(targetType, skope, true) == CompatibilityResult.REPORTED;
}
@Override
public boolean isCompatibleWith(TypeBinding targetType, final Scope skope) {
return internalIsCompatibleWith(targetType, skope, false) == CompatibilityResult.COMPATIBLE;
}
CompatibilityResult internalIsCompatibleWith(TypeBinding targetType, Scope skope, boolean reportShapeProblem) {
if (!super.isPertinentToApplicability(targetType, null))
return CompatibilityResult.COMPATIBLE;
LambdaExpression copy = null;
try {
copy = cachedResolvedCopy(targetType, argumentsTypeElided(), false, null, skope); // if argument types are elided, we don't care for result expressions against *this* target, any valid target is OK.
} catch (CopyFailureException cfe) {
if (this.assistNode)
return CompatibilityResult.COMPATIBLE; // can't type check result expressions, just say yes.
return isPertinentToApplicability(targetType, null) ? CompatibilityResult.INCOMPATIBLE : CompatibilityResult.COMPATIBLE; // don't expect to hit this ever.
}
if (copy == null)
return CompatibilityResult.INCOMPATIBLE;
// copy here is potentially compatible with the target type and has its shape fully computed: i.e value/void compatibility is determined and result expressions have been gathered.
targetType = findGroundTargetType(this.enclosingScope, targetType, targetType, argumentsTypeElided());
MethodBinding sam = targetType.getSingleAbstractMethod(this.enclosingScope, true);
if (sam == null || sam.problemId() == ProblemReasons.NoSuchSingleAbstractMethod) {
return CompatibilityResult.INCOMPATIBLE;
}
if (sam.returnType.id == TypeIds.T_void) {
if (!copy.voidCompatible) {
return CompatibilityResult.INCOMPATIBLE;
}
} else {
if (!copy.valueCompatible) {
if (reportShapeProblem) {
skope.problemReporter().missingValueFromLambda(this, sam.returnType);
return CompatibilityResult.REPORTED;
}
return CompatibilityResult.INCOMPATIBLE;
}
}
if (reportShapeProblem)
return CompatibilityResult.COMPATIBLE; // enough seen
if (!isPertinentToApplicability(targetType, null))
return CompatibilityResult.COMPATIBLE;
// catch up on one check deferred via skipKosherCheck=true (only if pertinent for applicability)
if (!kosherDescriptor(this.enclosingScope, sam, false))
return CompatibilityResult.INCOMPATIBLE;
Expression [] returnExpressions = copy.resultExpressions;
for (Expression returnExpression : returnExpressions) {
if (sam.returnType.isProperType(true) // inference variables can reach here during nested inference
&& this.enclosingScope.parameterCompatibilityLevel(returnExpression.resolvedType, sam.returnType) == Scope.NOT_COMPATIBLE) {
if (!returnExpression.isConstantValueOfTypeAssignableToType(returnExpression.resolvedType, sam.returnType))
if (sam.returnType.id != TypeIds.T_void || this.body instanceof Block)
return CompatibilityResult.INCOMPATIBLE;
}
}
return CompatibilityResult.COMPATIBLE;
}
static class CopyFailureException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
private LambdaExpression cachedResolvedCopy(TypeBinding targetType, boolean anyTargetOk, boolean requireExceptionAnalysis, InferenceContext18 context, Scope outerScope) {
if (this.committed && outerScope instanceof BlockScope) {
this.enclosingScope = (BlockScope) outerScope;
// trust the result of any previous shape analysis:
if (this.copiesPerTargetType != null && !this.copiesPerTargetType.isEmpty()) {
LambdaExpression firstCopy = this.copiesPerTargetType.values().iterator().next();
if (firstCopy != null) {
this.valueCompatible = firstCopy.valueCompatible;
this.voidCompatible = firstCopy.voidCompatible;
}
}
return this;
}
targetType = findGroundTargetType(this.enclosingScope, targetType, targetType, argumentsTypeElided());
if (targetType == null)
return null;
MethodBinding sam = targetType.getSingleAbstractMethod(this.enclosingScope, true);
if (sam == null || !sam.isValidBinding())
return null;
if (sam.parameters.length != this.arguments.length)
return null;
LambdaExpression copy = null;
if (this.copiesPerTargetType != null) {
copy = this.copiesPerTargetType.get(targetType);
if (copy == null) {
if (anyTargetOk && this.copiesPerTargetType.values().size() > 0)
copy = this.copiesPerTargetType.values().iterator().next();
}
}
IErrorHandlingPolicy oldPolicy = this.enclosingScope.problemReporter().switchErrorHandlingPolicy(silentErrorHandlingPolicy);
try {
if (copy == null) {
copy = copy();
if (copy == null)
throw new CopyFailureException();
if (InferenceContext18.DEBUG) {
System.out.println("Copy lambda "+this+" for target "+targetType.debugName()); //$NON-NLS-1$ //$NON-NLS-2$
}
copy.setExpressionContext(this.expressionContext);
copy.setExpectedType(targetType);
copy.inferenceContext = context;
TypeBinding type = copy.resolveType(this.enclosingScope, true);
if (type == null || !type.isValidBinding())
return null;
targetType = copy.expectedType; // possibly updated local types
if (this.copiesPerTargetType == null)
this.copiesPerTargetType = new HashMap<>();
this.copiesPerTargetType.put(targetType, copy);
}
if (!requireExceptionAnalysis)
return copy;
if (copy.thrownExceptions == null)
copy.analyzeExceptions();
return copy;
} finally {
this.enclosingScope.problemReporter().switchErrorHandlingPolicy(oldPolicy);
}
}
/**
* Get a resolved copy of this lambda for use by type inference, as to avoid spilling any premature
* type results into the original lambda.
*
* @param targetType the target functional type against which inference is attempted, must be a non-null valid functional type
* @return a resolved copy of 'this' or null if significant errors where encountered
*/
@Override
public LambdaExpression resolveExpressionExpecting(TypeBinding targetType, Scope skope, InferenceContext18 context) {
LambdaExpression copy = null;
try {
copy = cachedResolvedCopy(targetType, false, true, context, null /* to be safe we signal: not yet committed */);
} catch (CopyFailureException cfe) {
return null;
}
return copy;
}
@Override
public boolean sIsMoreSpecific(TypeBinding s, TypeBinding t, Scope skope) {
// 15.12.2.5
if (super.sIsMoreSpecific(s, t, skope))
return true;
if (argumentsTypeElided() || t.findSuperTypeOriginatingFrom(s) != null)
return false;
TypeBinding sPrime = s; // uncaptured
s = s.capture(this.enclosingScope, this.sourceStart, this.sourceEnd);
MethodBinding sSam = s.getSingleAbstractMethod(this.enclosingScope, true);
if (sSam == null || !sSam.isValidBinding())
return false;
MethodBinding tSam = t.getSingleAbstractMethod(this.enclosingScope, true);
if (tSam == null || !tSam.isValidBinding())
return true; // See ORT8.test450415a for a case that slips through isCompatibleWith.
MethodBinding adapted = tSam.computeSubstitutedMethod(sSam, skope.environment());
if (adapted == null) // not same type params
return false;
MethodBinding sSamPrime = sPrime.getSingleAbstractMethod(this.enclosingScope, true);
TypeBinding[] ps = adapted.parameters; // parameters of S adapted to type parameters of T
// parameters of S (without capture), adapted to type params of T
MethodBinding prime = tSam.computeSubstitutedMethod(sSamPrime, skope.environment());
TypeBinding[] pPrimes = prime.parameters;
TypeBinding[] qs = tSam.parameters;
for (int i = 0; i < ps.length; i++) {
if (!qs[i].isCompatibleWith(ps[i]) || TypeBinding.notEquals(qs[i], pPrimes[i]))
return false;
}
TypeBinding r1 = adapted.returnType; // return type of S adapted to type parameters of T
TypeBinding r2 = tSam.returnType;
if (r2.id == TypeIds.T_void)
return true;
if (r1.id == TypeIds.T_void)
return false;
// r1 <: r2
if (r1.isCompatibleWith(r2, skope))
return true;
LambdaExpression copy;
try {
copy = cachedResolvedCopy(s, true /* any resolved copy is good */, false, null, null /*not yet committed*/); // we expect a cached copy - otherwise control won't reach here.
} catch (CopyFailureException cfe) {
if (this.assistNode)
return false;
throw cfe;
}
Expression [] returnExpressions = copy.resultExpressions;
int returnExpressionsLength = returnExpressions == null ? 0 : returnExpressions.length;
if (returnExpressionsLength > 0) {
int i;
// r1 is a primitive type, r2 is a reference type, and each result expression is a standalone expression (15.2) of a primitive type
if (r1.isBaseType() && !r2.isBaseType()) {
for (i = 0; i < returnExpressionsLength; i++) {
if (returnExpressions[i].isPolyExpression() || !returnExpressions[i].resolvedType.isBaseType())
break;
}
if (i == returnExpressionsLength)
return true;
}
if (!r1.isBaseType() && r2.isBaseType()) {
for (i = 0; i < returnExpressionsLength; i++) {
if (returnExpressions[i].resolvedType.isBaseType())
break;
}
if (i == returnExpressionsLength)
return true;
}
if (r1.isFunctionalInterface(this.enclosingScope) && r2.isFunctionalInterface(this.enclosingScope)) {
for (i = 0; i < returnExpressionsLength; i++) {
Expression resultExpression = returnExpressions[i];
if (!resultExpression.sIsMoreSpecific(r1, r2, skope))
break;
}
if (i == returnExpressionsLength)
return true;
}
}
return false;
}
/**
* @return a virgin copy of `this' by reparsing the stashed textual form.
*/
LambdaExpression copy() {
final Parser parser = new Parser(this.enclosingScope.problemReporter(), false);
char [] source = new char [this.sourceEnd+1];
System.arraycopy(this.text, 0, source, this.sourceStart, this.sourceEnd - this.sourceStart + 1);
LambdaExpression copy = (LambdaExpression) parser.parseLambdaExpression(source,this.sourceStart, this.sourceEnd - this.sourceStart + 1,
this.enclosingScope.referenceCompilationUnit(), false /* record line separators */);
if (copy != null) { // ==> syntax errors == null
if (copy.sourceStart != this.sourceStart || copy.sourceEnd != this.sourceEnd)
return null; // something wrong
copy.original = this;
copy.assistNode = this.assistNode;
copy.enclosingScope = this.enclosingScope;
copy.text = this.text; // discard redundant textual copy
}
return copy;
}
public void returnsExpression(Expression expression, TypeBinding resultType) {
if (this.original == this) // Not in overload resolution context. result expressions not relevant.
return;
if (this.body instanceof Expression && ((Expression) this.body).isTrulyExpression()) {
this.valueCompatible = resultType != null && resultType.id == TypeIds.T_void ? false : true;
this.voidCompatible = this.assistNode ? true : ((Expression) this.body).statementExpression(); // while code is still being written and completed, we can't ask if it is a statement
this.resultExpressions = new Expression[] { expression };
return;
}
if (expression != null) {
this.returnsValue = true;
this.voidCompatible = false;
this.valueCompatible = !this.returnsVoid;
Expression [] returnExpressions = this.resultExpressions;
int resultsLength = returnExpressions.length;
System.arraycopy(returnExpressions, 0, returnExpressions = new Expression[resultsLength + 1], 0, resultsLength);
returnExpressions[resultsLength] = expression;
this.resultExpressions = returnExpressions;
} else {
this.returnsVoid = true;
this.valueCompatible = false;
this.voidCompatible = !this.returnsValue;
}
}
@Override
public CompilationResult compilationResult() {
return this.compilationResult;
}
@Override
public void abort(int abortLevel, CategorizedProblem problem) {
switch (abortLevel) {
case AbortCompilation :
throw new AbortCompilation(this.compilationResult, problem);
case AbortCompilationUnit :
throw new AbortCompilationUnit(this.compilationResult, problem);
case AbortType :
throw new AbortType(this.compilationResult, problem);
default :
throw new AbortMethod(this.compilationResult, problem);
}
}
@Override
public CompilationUnitDeclaration getCompilationUnitDeclaration() {
return this.enclosingScope == null ? null : this.enclosingScope.compilationUnitScope().referenceContext;
}
@Override
public boolean hasErrors() {
return this.ignoreFurtherInvestigation;
}
@Override
public void tagAsHavingErrors() {
this.ignoreFurtherInvestigation = true;
Scope parent = this.enclosingScope.parent;
while (parent != null) {
switch(parent.kind) {
case Scope.CLASS_SCOPE:
case Scope.METHOD_SCOPE:
ReferenceContext parentAST = parent.referenceContext();
if (parentAST != this) {
parentAST.tagAsHavingErrors();
return;
}
//$FALL-THROUGH$
default:
parent = parent.parent;
break;
}
}
}
public Set getThrownExceptions() {
if (this.thrownExceptions == null)
return Collections.emptySet();
return this.thrownExceptions;
}
public void generateCode(ClassScope classScope, ClassFile classFile) {
int problemResetPC = 0;
classFile.codeStream.wideMode = false;
boolean restart = false;
do {
try {
problemResetPC = classFile.contentsOffset;
this.generateCode(classFile);
restart = false;
} catch (AbortMethod e) {
// Restart code generation if possible ...
if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
// a branch target required a goto_w, restart code generation in wide mode.
classFile.contentsOffset = problemResetPC;
classFile.methodCount--;
classFile.codeStream.resetInWideMode(); // request wide mode
restart = true;
} else if (e.compilationResult == CodeStream.RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE) {
classFile.contentsOffset = problemResetPC;
classFile.methodCount--;
classFile.codeStream.resetForCodeGenUnusedLocals();
restart = true;
} else {
throw new AbortType(this.compilationResult, e.problem);
}
}
} while (restart);
}
public void generateCode(ClassFile classFile) {
classFile.generateMethodInfoHeader(this.binding);
int methodAttributeOffset = classFile.contentsOffset;
int attributeNumber = classFile.generateMethodInfoAttributes(this.binding);
int codeAttributeOffset = classFile.contentsOffset;
classFile.generateCodeAttributeHeader();
CodeStream codeStream = classFile.codeStream;
codeStream.reset(this, classFile);
// initialize local positions
this.scope.computeLocalVariablePositions(this.outerLocalVariablesSlotSize + (this.binding.isStatic() ? 0 : 1), codeStream);
if (this.outerLocalVariables != null) {
for (SyntheticArgumentBinding outerLocalVariable : this.outerLocalVariables) {
LocalVariableBinding argBinding;
codeStream.addVisibleLocalVariable(argBinding = outerLocalVariable);
codeStream.record(argBinding);
argBinding.recordInitializationStartPC(0);
}
}
// arguments initialization for local variable debug attributes
if (this.arguments != null) {
for (Argument argument : this.arguments) {
LocalVariableBinding argBinding;
codeStream.addVisibleLocalVariable(argBinding = argument.binding);
argBinding.recordInitializationStartPC(0);
}
}
codeStream.pushPatternAccessTrapScope(this.scope);
if (this.body instanceof Block) {
boolean prev = codeStream.stmtInPreConContext;
codeStream.stmtInPreConContext = this.inPreConstructorContext;
this.body.generateCode(this.scope, codeStream);
codeStream.stmtInPreConContext = prev;
if ((this.bits & ASTNode.NeedFreeReturn) != 0) {
codeStream.return_();
}
} else {
Expression expression = (Expression) this.body;
expression.generateCode(this.scope, codeStream, true);
if (this.binding.returnType == TypeBinding.VOID) {
codeStream.return_();
} else {
codeStream.generateReturnBytecode(expression);
}
}
// See https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1796#issuecomment-1933458054
codeStream.exitUserScope(this.scope, lvb -> !lvb.isParameter());
codeStream.handleRecordAccessorExceptions(this.scope);
// local variable attributes
codeStream.exitUserScope(this.scope);
codeStream.recordPositionsFrom(0, this.sourceEnd); // WAS declarationSourceEnd.
try {
classFile.completeCodeAttribute(codeAttributeOffset, this.scope);
} catch(NegativeArraySizeException e) {
throw new AbortMethod(this.scope.referenceCompilationUnit().compilationResult, null);
}
attributeNumber++;
classFile.completeMethodInfo(this.binding, methodAttributeOffset, attributeNumber);
}
public void addSyntheticArgument(LocalVariableBinding actualOuterLocalVariable) {
if (this.original != this || this.binding == null)
return; // Do not bother tracking outer locals for clones created during overload resolution.
SyntheticArgumentBinding syntheticLocal = null;
int newSlot = this.outerLocalVariables.length;
for (int i = 0; i < newSlot; i++) {
if (this.outerLocalVariables[i].actualOuterLocalVariable == actualOuterLocalVariable)
return;
}
System.arraycopy(this.outerLocalVariables, 0, this.outerLocalVariables = new SyntheticArgumentBinding[newSlot + 1], 0, newSlot);
this.outerLocalVariables[newSlot] = syntheticLocal = new SyntheticArgumentBinding(actualOuterLocalVariable);
syntheticLocal.resolvedPosition = this.outerLocalVariablesSlotSize; // may need adjusting later if we need to generate an instance method for the lambda.
syntheticLocal.declaringScope = this.scope;
int parameterCount = this.binding.parameters.length;
TypeBinding [] newParameters = new TypeBinding[parameterCount + 1];
newParameters[newSlot] = actualOuterLocalVariable.type;
for (int i = 0, j = 0; i < parameterCount; i++, j++) {
if (i == newSlot) j++;
newParameters[j] = this.binding.parameters[i];
}
this.binding.parameters = newParameters;
switch (syntheticLocal.type.id) {
case TypeIds.T_long :
case TypeIds.T_double :
this.outerLocalVariablesSlotSize += 2;
break;
default :
this.outerLocalVariablesSlotSize++;
break;
}
}
public SyntheticArgumentBinding addSyntheticArgument(ReferenceBinding enclosingType) {
if (this.original != this || this.binding == null)
return null; // Do not bother tracking outer locals for clones created during overload resolution.
SyntheticArgumentBinding syntheticLocal = null;
int newSlot = this.outerLocalVariables.length;
System.arraycopy(this.outerLocalVariables, 0, this.outerLocalVariables = new SyntheticArgumentBinding[newSlot + 1], 0, newSlot);
this.outerLocalVariables[newSlot] = syntheticLocal = new SyntheticArgumentBinding(enclosingType);
syntheticLocal.resolvedPosition = this.outerLocalVariablesSlotSize; // may need adjusting later if we need to generate an instance method for the lambda.
syntheticLocal.declaringScope = this.scope;
int parameterCount = this.binding.parameters.length;
TypeBinding [] newParameters = new TypeBinding[parameterCount + 1];
newParameters[newSlot] = enclosingType;
for (int i = 0, j = 0; i < parameterCount; i++, j++) {
if (i == newSlot) j++;
newParameters[j] = this.binding.parameters[i];
}
this.binding.parameters = newParameters;
switch (syntheticLocal.type.id) {
case TypeIds.T_long :
case TypeIds.T_double :
this.outerLocalVariablesSlotSize += 2;
break;
default :
this.outerLocalVariablesSlotSize++;
break;
}
return syntheticLocal;
}
public SyntheticArgumentBinding getSyntheticArgument(LocalVariableBinding actualOuterLocalVariable) {
for (int i = 0, length = this.outerLocalVariables == null ? 0 : this.outerLocalVariables.length; i < length; i++)
if (this.outerLocalVariables[i].actualOuterLocalVariable == actualOuterLocalVariable)
return this.outerLocalVariables[i];
return null;
}
// Return the actual method binding devoid of synthetics.
@Override
public MethodBinding getMethodBinding() {
if (this.actualMethodBinding == null) {
if (this.binding != null) {
// Get rid of the synthetic arguments added via addSyntheticArgument()
TypeBinding[] newParams = null;
if (this.binding instanceof SyntheticMethodBinding && this.outerLocalVariables.length > 0) {
newParams = new TypeBinding[this.binding.parameters.length - this.outerLocalVariables.length];
System.arraycopy(this.binding.parameters, this.outerLocalVariables.length, newParams, 0, newParams.length);
} else {
newParams = this.binding.parameters;
}
this.actualMethodBinding = new MethodBinding(this.binding.modifiers, this.binding.selector,
this.binding.returnType, newParams, this.binding.thrownExceptions, this.binding.declaringClass);
this.actualMethodBinding.tagBits = this.binding.tagBits;
} else {
this.actualMethodBinding = new ProblemMethodBinding(CharOperation.NO_CHAR, null, ProblemReasons.NoSuchSingleAbstractMethod);
}
}
return this.actualMethodBinding;
}
@Override
public int diagnosticsSourceEnd() {
return this.body instanceof Block ? this.arrowPosition : this.sourceEnd;
}
public TypeBinding[] getMarkerInterfaces() {
if (this.expectedType instanceof IntersectionTypeBinding18) {
Set markerBindings = new LinkedHashSet();
IntersectionTypeBinding18 intersectionType = (IntersectionTypeBinding18)this.expectedType;
TypeBinding[] intersectionTypes = intersectionType.intersectingTypes;
TypeBinding samType = intersectionType.getSAMType(this.enclosingScope);
for (TypeBinding typeBinding : intersectionTypes) {
if (!typeBinding.isInterface() // only interfaces
|| TypeBinding.equalsEquals(samType, typeBinding) // except for the samType itself
|| typeBinding.id == TypeIds.T_JavaIoSerializable) // but Serializable is captured as a bitflag
{
continue;
}
markerBindings.add(typeBinding);
}
if (markerBindings.size() > 0) {
return (TypeBinding[])markerBindings.toArray(new TypeBinding[markerBindings.size()]);
}
}
return null;
}
public ReferenceBinding getTypeBinding() {
if (this.classType != null || this.resolvedType == null)
return null;
class LambdaTypeBinding extends ReferenceBinding {
@Override
public MethodBinding[] methods() {
return new MethodBinding [] { getMethodBinding() };
}
@Override
public char[] sourceName() {
return TypeConstants.LAMBDA_TYPE;
}
@Override
public ReferenceBinding superclass() {
return LambdaExpression.this.scope.getJavaLangObject();
}
@Override
public ReferenceBinding[] superInterfaces() {
return new ReferenceBinding[] { (ReferenceBinding) LambdaExpression.this.resolvedType };
}
@Override
public char[] computeUniqueKey() {
return LambdaExpression.this.descriptor.declaringClass.computeUniqueKey();
}
@Override
public String toString() {
StringBuilder output = new StringBuilder("()->{} implements "); //$NON-NLS-1$
output.append(LambdaExpression.this.descriptor.declaringClass.sourceName());
output.append('.');
output.append(LambdaExpression.this.descriptor.toString());
return output.toString();
}
}
return this.classType = new LambdaTypeBinding();
}
public void addLocalType(LocalTypeBinding localTypeBinding) {
if (this.localTypes == null)
this.localTypes = new HashMap<>();
this.localTypes.put(localTypeBinding.sourceStart, localTypeBinding);
}
/**
* During inference, several copies of a lambda may be created.
* If a lambda body contains a local type declaration, one binding may be created
* within each of the lambda copies. Once inference finished, we need to map all
* such local type bindings to the instance from the correct lambda copy.
*
* When a local type binding occurs as a field of another type binding (e.g.,
* type argument), the local type will be replaced in-place, assuming that the
* previous binding should never escape the context of resolving this lambda.
*
*/
static class LocalTypeSubstitutor extends Substitutor {
Map localTypes2;
public LocalTypeSubstitutor(Map localTypes, MethodBinding methodBinding) {
this.localTypes2 = localTypes;
if (methodBinding != null && methodBinding.isStatic())
this.staticContext = methodBinding.declaringClass;
}
@Override
public TypeBinding substitute(Substitution substitution, TypeBinding originalType) {
if (originalType.isLocalType()) {
LocalTypeBinding orgLocal = (LocalTypeBinding) originalType.original();
MethodScope lambdaScope2 = orgLocal.scope.enclosingLambdaScope();
if (lambdaScope2 != null) {
// local type within a lambda may need replacement:
TypeBinding substType = this.localTypes2.get(orgLocal.sourceStart);
if (substType != null && substType != orgLocal) { //$IDENTITY-COMPARISON$
orgLocal.transferConstantPoolNameTo(substType);
return substType;
}
}
return originalType;
}
return super.substitute(substitution, originalType);
}
}
boolean updateLocalTypes() {
if (this.descriptor == null || this.localTypes == null)
return false;
LocalTypeSubstitutor substor = new LocalTypeSubstitutor(this.localTypes, null/*lambda method is never static*/);
NullSubstitution subst = new NullSubstitution(this.scope.environment());
updateLocalTypesInMethod(this.binding, substor, subst);
updateLocalTypesInMethod(this.descriptor, substor, subst);
this.resolvedType = substor.substitute(subst, this.resolvedType);
this.expectedType = substor.substitute(subst, this.expectedType);
return true;
}
/**
* Perform substitution with a {@link LocalTypeSubstitutor} on all types mentioned in the given method binding.
*/
boolean updateLocalTypesInMethod(MethodBinding method) {
if (this.localTypes == null)
return false;
updateLocalTypesInMethod(method, new LocalTypeSubstitutor(this.localTypes, method), new NullSubstitution(this.scope.environment()));
return true;
}
public static void updateLocalTypesInMethod(MethodBinding method, Substitutor substor, Substitution subst) {
method.declaringClass = (ReferenceBinding) substor.substitute(subst, method.declaringClass);
method.returnType = substor.substitute(subst, method.returnType);
for (int i = 0; i < method.parameters.length; i++) {
method.parameters[i] = substor.substitute(subst, method.parameters[i]);
}
if (method instanceof ParameterizedGenericMethodBinding) {
ParameterizedGenericMethodBinding pgmb = (ParameterizedGenericMethodBinding) method;
for (int i = 0; i < pgmb.typeArguments.length; i++) {
pgmb.typeArguments[i] = substor.substitute(subst, pgmb.typeArguments[i]);
}
}
}
}