blob: 2ce4373aba764d7b5fd73792b2318e51cc0e6654 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This is an implementation of an early-draft specification developed under the Java
* Community Process (JCP) and is made available for testing and evaluation purposes
* only. The code is not compatible with any specification of the JCP.
*
* 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
* Stephan Herrmann - Contribution for
* bug 401030 - [1.8][null] Null analysis support for lambda methods.
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.IProblem;
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.FlowContext;
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.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.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.PolyTypeBinding;
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.SyntheticArgumentBinding;
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.VariableBinding;
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;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
public class LambdaExpression extends FunctionalExpression implements ReferenceContext, ProblemSeverities {
public Argument [] arguments;
public Statement body;
public boolean hasParentheses;
public MethodScope scope;
private boolean voidCompatible = true;
private boolean valueCompatible = false;
private boolean shapeAnalysisComplete = false;
private boolean returnsValue;
private boolean returnsVoid;
private boolean throwsException;
private LambdaExpression original = this;
private SyntheticArgumentBinding[] outerLocalVariables = NO_SYNTHETIC_ARGUMENTS;
private int outerLocalVariablesSlotSize = 0;
public boolean shouldCaptureInstance = false;
private static final SyntheticArgumentBinding [] NO_SYNTHETIC_ARGUMENTS = new SyntheticArgumentBinding[0];
public LambdaExpression(CompilationResult compilationResult, Argument [] arguments, Statement body) {
super(compilationResult);
this.arguments = arguments != null ? arguments : ASTNode.NO_ARGUMENTS;
this.body = body;
}
protected FunctionalExpression original() {
return this.original;
}
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();
this.binding = sourceType.addSyntheticMethod(this);
int pc = codeStream.position;
StringBuffer signature = new StringBuffer();
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) {
syntheticArgument.resolvedPosition++;
}
signature.append(syntheticArgument.type.signature());
LocalVariableBinding capturedOuterLocal = syntheticArgument.actualOuterLocalVariable;
VariableBinding[] path = currentScope.getEmulationPath(capturedOuterLocal);
codeStream.generateOuterAccess(path, this, capturedOuterLocal, currentScope);
}
signature.append(')');
signature.append(this.expectedType.signature());
int invokeDynamicNumber = codeStream.classFile.recordBootstrapMethod(this);
codeStream.invokeDynamic(invokeDynamicNumber, (this.shouldCaptureInstance ? 1 : 0) + this.outerLocalVariablesSlotSize, 1, TypeConstants.ANONYMOUS_METHOD, signature.toString().toCharArray());
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
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);
}
/* 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)
*/
public TypeBinding resolveType(BlockScope blockScope) {
this.constant = Constant.NotAConstant;
this.enclosingScope = blockScope;
if (this.expectedType == null && this.expressionContext == INVOCATION_CONTEXT) {
return new PolyTypeBinding(this);
}
MethodScope methodScope = blockScope.methodScope();
this.scope = new MethodScope(blockScope, this, methodScope.isStatic);
this.scope.isConstructorCall = methodScope.isConstructorCall;
super.resolveType(blockScope); // compute & capture interface function descriptor in singleAbstractMethod.
final boolean argumentsTypeElided = argumentsTypeElided();
final boolean haveDescriptor = this.descriptor != null;
if (haveDescriptor && this.descriptor.typeVariables != Binding.NO_TYPE_VARIABLES) // already complained in kosher*
return null;
if (!haveDescriptor && argumentsTypeElided)
return null; // FUBAR, bail out...
this.binding = new MethodBinding(ClassFileConstants.AccPrivate | ClassFileConstants.AccSynthetic | ExtraCompilerModifiers.AccUnresolved,
TypeConstants.ANONYMOUS_METHOD, // will be fixed up later.
haveDescriptor ? this.descriptor.returnType : null,
Binding.NO_PARAMETERS, // for now.
haveDescriptor ? this.descriptor.thrownExceptions : Binding.NO_EXCEPTIONS,
blockScope.enclosingSourceType());
this.binding.typeVariables = Binding.NO_TYPE_VARIABLES;
if (haveDescriptor) {
int descriptorParameterCount = this.descriptor.parameters.length;
int lambdaArgumentCount = this.arguments != null ? this.arguments.length : 0;
if (descriptorParameterCount != lambdaArgumentCount) {
this.scope.problemReporter().lambdaSignatureMismatched(this);
if (argumentsTypeElided)
return null; // FUBAR, bail out ...
}
}
boolean buggyArguments = false;
int length = this.arguments == null ? 0 : this.arguments.length;
TypeBinding[] newParameters = new TypeBinding[length];
AnnotationBinding [][] parameterAnnotations = null;
for (int i = 0; i < length; i++) {
Argument argument = this.arguments[i];
if (argument.isVarArgs()) {
if (i == length - 1) {
this.binding.modifiers |= ClassFileConstants.AccVarargs;
} else {
this.scope.problemReporter().illegalVarargInLambda(argument);
buggyArguments = true;
}
}
TypeBinding parameterType;
final TypeBinding expectedParameterType = haveDescriptor && i < this.descriptor.parameters.length ? this.descriptor.parameters[i] : null;
parameterType = argumentsTypeElided ? expectedParameterType : argument.type.resolveType(this.scope, true /* check bounds*/);
if (parameterType == null) {
buggyArguments = true;
} else if (parameterType == TypeBinding.VOID) {
this.scope.problemReporter().argumentTypeCannotBeVoid(this, argument);
buggyArguments = true;
} else {
if (!parameterType.isValidBinding()) {
this.binding.tagBits |= TagBits.HasUnresolvedArguments;
}
if ((parameterType.tagBits & TagBits.HasMissingType) != 0) {
this.binding.tagBits |= TagBits.HasMissingType;
}
if (haveDescriptor && expectedParameterType != null && parameterType.isValidBinding() && parameterType != expectedParameterType) {
this.scope.problemReporter().lambdaParameterTypeMismatched(argument, argument.type, expectedParameterType);
}
TypeBinding leafType = parameterType.leafComponentType();
if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0)
this.binding.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
newParameters[i] = parameterType;
argument.bind(this.scope, parameterType, false);
if (argument.annotations != null) {
this.binding.tagBits |= TagBits.HasParameterAnnotations;
if (parameterAnnotations == null) {
parameterAnnotations = new AnnotationBinding[length][];
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;
}
}
}
// only assign parameters if no problems are found
if (!buggyArguments) {
this.binding.parameters = newParameters;
if (parameterAnnotations != null)
this.binding.setParameterAnnotations(parameterAnnotations);
}
if (!argumentsTypeElided && 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;
length = exceptions.length;
for (int i = 0; i < length; i++) {
ReferenceBinding exception = exceptions[i];
if ((exception.tagBits & TagBits.HasMissingType) != 0) {
this.binding.tagBits |= TagBits.HasMissingType;
}
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;
}
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 && blockScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
if (!argumentsTypeElided) {
AbstractMethodDeclaration.createArgumentBindings(this.arguments, this.binding, this.scope);
validateNullAnnotations();
// 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;
if (this.body instanceof Expression) {
Expression expression = (Expression) this.body;
new ReturnStatement(expression, expression.sourceStart, expression.sourceEnd, true).resolve(this.scope); // :-) ;-)
} else {
this.body.resolve(this.scope);
}
return this.resolvedType;
}
private boolean argumentsTypeElided() {
return this.arguments.length > 0 && this.arguments[0].hasElidedType();
}
private boolean doesNotCompleteNormally() {
return this.body.analyseCode(this.scope,
new ExceptionHandlingFlowContext(null, this, Binding.NO_EXCEPTIONS, null, this.scope, FlowInfo.DEAD_END),
FlowInfo.initial(this.scope.referenceType().maxFieldCount)) == FlowInfo.DEAD_END;
}
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,
null,
this.scope,
FlowInfo.DEAD_END);
// nullity and mark as assigned
MethodBinding methodWithParameterDeclaration = argumentsTypeElided() ? this.descriptor : this.binding;
AbstractMethodDeclaration.analyseArguments(lambdaInfo, this.arguments, methodWithParameterDeclaration);
if (this.arguments != null) {
for (int i = 0, count = this.arguments.length; i < count; i++) {
this.bits |= (this.arguments[i].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 { // Expression
if (currentScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled
&& flowInfo.reachMode() == FlowInfo.REACHABLE)
{
Expression expression = (Expression)this.body;
checkAgainstNullAnnotation(flowContext, expression, expression.nullStatus(flowInfo, flowContext));
}
}
return flowInfo;
}
// cf. AbstractMethodDeclaration.validateNullAnnotations()
// pre: !argumentTypeElided()
void validateNullAnnotations() {
// null annotations on parameters?
if (this.binding != null && this.binding.parameterNonNullness != null) {
int length = this.binding.parameters.length;
for (int i=0; i<length; i++) {
if (this.binding.parameterNonNullness[i] != null) {
long nullAnnotationTagBit = this.binding.parameterNonNullness[i].booleanValue()
? TagBits.AnnotationNonNull : TagBits.AnnotationNullable;
this.scope.validateNullAnnotation(nullAnnotationTagBit, this.arguments[i].type, this.arguments[i].annotations);
}
}
}
}
// pre: !argumentTypeElided()
// try to merge null annotations from descriptor into binding, complaining about any incompatibilities found
private void mergeParameterNullAnnotations(BlockScope currentScope) {
if (this.descriptor.parameterNonNullness == null)
return;
if (this.binding.parameterNonNullness == null) {
this.binding.parameterNonNullness = this.descriptor.parameterNonNullness;
return;
}
LookupEnvironment env = currentScope.environment();
Boolean[] ourNonNullness = this.binding.parameterNonNullness;
Boolean[] descNonNullness = this.descriptor.parameterNonNullness;
int len = Math.min(ourNonNullness.length, descNonNullness.length);
for (int i = 0; i < len; i++) {
if (ourNonNullness[i] == null) {
ourNonNullness[i] = descNonNullness[i];
} else if (ourNonNullness[i] != descNonNullness[i]) {
if (ourNonNullness[i] == Boolean.TRUE) { // requested @NonNull not provided
char[][] inheritedAnnotationName = null;
if (descNonNullness[i] == Boolean.FALSE)
inheritedAnnotationName = env.getNullableAnnotationName();
currentScope.problemReporter().illegalRedefinitionToNonNullParameter(this.arguments[i], this.descriptor.declaringClass, inheritedAnnotationName);
}
}
}
}
// simplified version of ReturnStatement.checkAgainstNullAnnotation()
void checkAgainstNullAnnotation(FlowContext flowContext, Expression expression, int nullStatus) {
if (nullStatus != FlowInfo.NON_NULL) {
// if we can't prove non-null check against declared null-ness of the descriptor method:
// Note that this.binding never has a return type declaration, always inherit null-ness from the descriptor
if ((this.descriptor.tagBits & TagBits.AnnotationNonNull) != 0) {
flowContext.recordNullityMismatch(this.scope, expression, expression.resolvedType, this.descriptor.returnType, nullStatus);
}
}
}
public StringBuffer printExpression(int tab, StringBuffer output) {
int parenthesesCount = (this.bits & ASTNode.ParenthesizedMASK) >> 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$
this.body.print(this.body instanceof Block ? tab : 0, output);
return output.append(suffix);
}
public TypeBinding expectedResultType() {
return this.descriptor != null && this.descriptor.isValidBinding() ? this.descriptor.returnType : null;
}
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;
}
protected boolean errorEqualsIncompatibility() {
return this.original.shapeAnalysisComplete; // so as not to abort shape analysis.
}
public boolean isCompatibleWith(final TypeBinding left, final Scope someScope) {
final MethodBinding sam = left.getSingleAbstractMethod(this.enclosingScope);
if (sam == null || !sam.isValidBinding())
return false;
if (sam.parameters.length != this.arguments.length)
return false;
if (!this.shapeAnalysisComplete && this.body instanceof Expression) {
Expression expression = (Expression) this.body;
this.voidCompatible = expression.statementExpression();
this.valueCompatible = true;
this.shapeAnalysisComplete = true;
}
if (this.shapeAnalysisComplete) {
if (squarePegInRoundHole(sam))
return false;
}
IErrorHandlingPolicy oldPolicy = this.enclosingScope.problemReporter().switchErrorHandlingPolicy(silentErrorHandlingPolicy);
this.hasIgnoredMandatoryErrors = false;
try {
final LambdaExpression copy = copy();
if (copy == null)
return false;
copy.setExpressionContext(this.expressionContext);
copy.setExpectedType(left);
if (this.resultExpressions == null)
this.resultExpressions = new SimpleLookupTable(); // gather result expressions for most specific method analysis.
this.resultExpressions.put(left, new Expression[0]);
copy.resolveType(this.enclosingScope);
if (!this.shapeAnalysisComplete) {
boolean lambdaIsFubar = this.hasIgnoredMandatoryErrors; // capture now, before doesNotCompleteNormally which runs analyzeCode on lambda body *without* the enclosing context being analyzed
if (!this.returnsVoid && !this.returnsValue && this.throwsException) { // () -> { throw new Exception(); } is value compatible.
Block block = (Block) this.body;
final Statement[] statements = block.statements;
final int statementsLength = statements == null ? 0 : statements.length;
Statement ultimateStatement = statementsLength == 0 ? null : statements[statementsLength - 1];
this.valueCompatible = ultimateStatement instanceof ThrowStatement ? true: copy.doesNotCompleteNormally();
}
this.shapeAnalysisComplete = true;
if (squarePegInRoundHole(sam) || lambdaIsFubar)
return false;
}
} catch (IncongruentLambdaException e) {
return false;
} finally {
this.enclosingScope.problemReporter().switchErrorHandlingPolicy(oldPolicy);
this.hasIgnoredMandatoryErrors = false;
}
return true;
}
public boolean tIsMoreSpecific(TypeBinding t, TypeBinding s) {
/* 15.12.2.5 t is more specific than s iff ... Some of the checks here are redundant by the very fact of control reaching here,
but have been left in for completeness/documentation sakes. These should be cheap anyways.
*/
// Both t and s are functional interface types ...
MethodBinding tSam = t.getSingleAbstractMethod(this.enclosingScope);
if (tSam == null || !tSam.isValidBinding())
return false;
MethodBinding sSam = s.getSingleAbstractMethod(this.enclosingScope);
if (sSam == null || !sSam.isValidBinding())
return false;
// t should neither be a subinterface nor a superinterface of s
if (t.findSuperTypeOriginatingFrom(s) != null || s.findSuperTypeOriginatingFrom(t) != null)
return false;
// If the lambda expression's parameters have inferred types, then the descriptor parameter types of t are the same as the descriptor parameter types of s.
if (argumentsTypeElided()) {
if (tSam.parameters.length != sSam.parameters.length)
return false;
for (int i = 0, length = tSam.parameters.length; i < length; i++) {
if (tSam.parameters[i] != sSam.parameters[i])
return false;
}
}
// either the descriptor return type of s is void or ...
if (sSam.returnType.id == TypeIds.T_void)
return true;
/* ... or for all result expressions in the lambda body (or for the body itself if the body is an expression),
the descriptor return type of the capture of T is more specific than the descriptor return type of S.
*/
Expression [] returnExpressions = (Expression[]) this.resultExpressions.get(t); // should be same as for s
int returnExpressionsLength = returnExpressions == null ? 0 : returnExpressions.length;
if (returnExpressionsLength == 0)
return true; // as good as or as bad as false.
t = t.capture(this.enclosingScope, this.sourceEnd);
tSam = t.getSingleAbstractMethod(this.enclosingScope);
for (int i = 0; i < returnExpressionsLength; i++) {
Expression resultExpression = returnExpressions[i];
if (!resultExpression.tIsMoreSpecific(tSam.returnType, sSam.returnType))
return false;
}
return true;
}
private boolean squarePegInRoundHole(final MethodBinding sam) {
if (sam.returnType.id == TypeIds.T_void) {
if (!this.voidCompatible)
return true;
} else {
if (!this.valueCompatible)
return true;
}
return false;
}
LambdaExpression copy() {
final Parser parser = new Parser(this.enclosingScope.problemReporter(), false);
final char[] source = this.compilationResult.getCompilationUnit().getContents();
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
copy.original = this;
}
return copy;
}
public void returnsExpression(Expression expression, TypeBinding resultType) {
if (this.original == this) // not in overload resolution context.
return;
if (expression != null) {
this.original.returnsValue = true;
this.original.voidCompatible = false;
this.original.valueCompatible = !this.original.returnsVoid;
if (resultType != null) {
Expression [] results = (Expression[]) this.original.resultExpressions.get(this.expectedType);
int resultsLength = results.length;
System.arraycopy(results, 0, results = new Expression[resultsLength + 1], 0, resultsLength);
results[resultsLength] = expression;
}
} else {
this.original.returnsVoid = true;
this.original.valueCompatible = false;
this.original.voidCompatible = !this.original.returnsValue;
}
}
public void throwsException(TypeBinding exceptionType) {
if (this.expressionContext != INVOCATION_CONTEXT)
return;
this.original.throwsException = true;
}
public CompilationResult compilationResult() {
return this.compilationResult;
}
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);
}
}
public CompilationUnitDeclaration getCompilationUnitDeclaration() {
return this.enclosingScope == null ? null : this.enclosingScope.compilationUnitScope().referenceContext;
}
public boolean hasErrors() {
return this.ignoreFurtherInvestigation;
}
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:
parent.referenceContext().tagAsHavingErrors();
return;
default:
parent = parent.parent;
break;
}
}
}
public void tagAsHavingIgnoredMandatoryErrors(int problemId) {
// 15.27.3 requires exception throw related errors to not influence congruence. Other errors should. Also don't abort shape analysis.
switch (problemId) {
case IProblem.UnhandledExceptionOnAutoClose:
case IProblem.UnhandledExceptionInDefaultConstructor:
case IProblem.UnhandledException:
return;
default:
if (errorEqualsIncompatibility())
throw new IncongruentLambdaException();
this.original().hasIgnoredMandatoryErrors = true;
return;
}
}
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 (int i = 0, max = this.outerLocalVariables.length; i < max; i++) {
LocalVariableBinding argBinding;
codeStream.addVisibleLocalVariable(argBinding = this.outerLocalVariables[i]);
codeStream.record(argBinding);
argBinding.recordInitializationStartPC(0);
}
}
// arguments initialization for local variable debug attributes
if (this.arguments != null) {
for (int i = 0, max = this.arguments.length; i < max; i++) {
LocalVariableBinding argBinding;
codeStream.addVisibleLocalVariable(argBinding = this.arguments[i].binding);
argBinding.recordInitializationStartPC(0);
}
}
if (this.body instanceof Block) {
this.body.generateCode(this.scope, codeStream);
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);
}
}
// local variable attributes
codeStream.exitUserScope(this.scope);
codeStream.recordPositionsFrom(0, this.sourceEnd); // WAS declarationSourceEnd.
try {
classFile.completeCodeAttribute(codeAttributeOffset);
} 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 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;
}
}
class IncongruentLambdaException extends RuntimeException {
private static final long serialVersionUID = 4145723509219836114L;
}