package org.eclipse.jdt.internal.compiler.ast;
import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.ASSIGNMENT_CONTEXT;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class ReturnStatement extends Statement {
public Expression expression;
public SubRoutineStatement[] subroutines;
public LocalVariableBinding saveValueVariable;
public int initStateIndex = -1;
private boolean implicitReturn;
public ReturnStatement(Expression expression, int sourceStart, int sourceEnd) {
this(expression, sourceStart, sourceEnd, false);
}
public ReturnStatement(Expression expression, int sourceStart, int sourceEnd, boolean implicitReturn) {
this.sourceStart = sourceStart;
this.sourceEnd = sourceEnd;
this.expression = expression;
this.implicitReturn = implicitReturn;
}
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
if (this.expression instanceof FunctionalExpression) {
if (this.expression.resolvedType == null || !this.expression.resolvedType.isValidBinding()) {
flowContext.recordAbruptExit();
return FlowInfo.DEAD_END;
}
}
MethodScope methodScope = currentScope.methodScope();
if (this.expression != null) {
flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo);
this.expression.checkNPEbyUnboxing(currentScope, flowContext, flowInfo);
if (flowInfo.reachMode() == FlowInfo.REACHABLE && currentScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled)
checkAgainstNullAnnotation(currentScope, flowContext, flowInfo, this.expression);
if (currentScope.compilerOptions().analyseResourceLeaks) {
FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.expression, flowInfo, flowContext);
if (trackingVariable != null) {
if (methodScope != trackingVariable.methodScope)
trackingVariable.markClosedInNestedMethod();
flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.expression, flowInfo, flowContext, true);
}
}
}
this.initStateIndex =
methodScope.recordInitializationStates(flowInfo);
FlowContext traversedContext = flowContext;
int subCount = 0;
boolean saveValueNeeded = false;
boolean hasValueToSave = needValueStore();
boolean noAutoCloseables = true;
do {
SubRoutineStatement sub;
if ((sub = traversedContext.subroutine()) != null) {
if (this.subroutines == null){
this.subroutines = new SubRoutineStatement[5];
}
if (subCount == this.subroutines.length) {
System.arraycopy(this.subroutines, 0, (this.subroutines = new SubRoutineStatement[subCount*2]), 0, subCount);
}
this.subroutines[subCount++] = sub;
if (sub.isSubRoutineEscaping()) {
saveValueNeeded = false;
this.bits |= ASTNode.IsAnySubRoutineEscaping;
break;
}
if (sub instanceof TryStatement) {
if (((TryStatement) sub).resources.length > 0) {
noAutoCloseables = false;
}
}
}
traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
if (traversedContext instanceof InsideSubRoutineFlowContext) {
ASTNode node = traversedContext.associatedNode;
if (node instanceof SynchronizedStatement) {
this.bits |= ASTNode.IsSynchronized;
} else if (node instanceof TryStatement) {
TryStatement tryStatement = (TryStatement) node;
flowInfo.addInitializationsFrom(tryStatement.subRoutineInits);
if (hasValueToSave) {
if (this.saveValueVariable == null){
prepareSaveValueLocation(tryStatement);
}
saveValueNeeded = true;
this.initStateIndex =
methodScope.recordInitializationStates(flowInfo);
}
}
} else if (traversedContext instanceof InitializationFlowContext) {
currentScope.problemReporter().cannotReturnInInitializer(this);
return FlowInfo.DEAD_END;
}
} while ((traversedContext = traversedContext.getLocalParent()) != null);
if ((this.subroutines != null) && (subCount != this.subroutines.length)) {
System.arraycopy(this.subroutines, 0, (this.subroutines = new SubRoutineStatement[subCount]), 0, subCount);
}
if (saveValueNeeded) {
if (this.saveValueVariable != null) {
this.saveValueVariable.useFlag = LocalVariableBinding.USED;
}
} else {
this.saveValueVariable = null;
if (((this.bits & ASTNode.IsSynchronized) == 0) && this.expression != null && TypeBinding.equalsEquals(this.expression.resolvedType, TypeBinding.BOOLEAN)) {
if (noAutoCloseables) {
this.expression.bits |= ASTNode.IsReturnedValue;
}
}
}
currentScope.checkUnclosedCloseables(flowInfo, flowContext, this, currentScope);
flowContext.recordAbruptExit();
flowContext.expireNullCheckedFieldInfo();
return FlowInfo.DEAD_END;
}
@Override
public boolean doesNotCompleteNormally() {
return true;
}
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream) {
if ((this.bits & ASTNode.IsReachable) == 0) {
return;
}
int pc = codeStream.position;
boolean alreadyGeneratedExpression = false;
if (needValueStore()) {
alreadyGeneratedExpression = true;
this.expression.generateCode(currentScope, codeStream, needValue());
generateStoreSaveValueIfNecessary(currentScope, codeStream);
}
if (this.subroutines != null) {
Object reusableJSRTarget = this.expression == null ? (Object)TypeBinding.VOID : this.expression.reusableJSRTarget();
for (int i = 0, max = this.subroutines.length; i < max; i++) {
SubRoutineStatement sub = this.subroutines[i];
boolean didEscape = sub.generateSubRoutineInvocation(currentScope, codeStream, reusableJSRTarget, this.initStateIndex, this.saveValueVariable);
if (didEscape) {
codeStream.recordPositionsFrom(pc, this.sourceStart);
SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, i, codeStream);
return;
}
}
}
if (this.saveValueVariable != null) {
codeStream.load(this.saveValueVariable);
}
if (this.expression != null && !alreadyGeneratedExpression) {
this.expression.generateCode(currentScope, codeStream, true);
generateStoreSaveValueIfNecessary(currentScope, codeStream);
}
generateReturnBytecode(codeStream);
if (this.saveValueVariable != null) {
codeStream.removeVariable(this.saveValueVariable);
}
if (this.initStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.initStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.initStateIndex);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, -1, codeStream);
}
public void generateReturnBytecode(CodeStream codeStream) {
codeStream.generateReturnBytecode(this.expression);
}
public void generateStoreSaveValueIfNecessary(Scope scope, CodeStream codeStream){
if (this.saveValueVariable != null) {
codeStream.store(this.saveValueVariable, false);
codeStream.addVariable(this.saveValueVariable);
}
}
private boolean needValueStore() {
return this.expression != null
&& (this.expression.constant == Constant.NotAConstant || (this.expression.implicitConversion & TypeIds.BOXING)!= 0)
&& !(this.expression instanceof NullLiteral);
}
public boolean needValue() {
return this.saveValueVariable != null
|| (this.bits & ASTNode.IsSynchronized) != 0
|| ((this.bits & ASTNode.IsAnySubRoutineEscaping) == 0);
}
public void prepareSaveValueLocation(TryStatement targetTryStatement){
this.saveValueVariable = targetTryStatement.secretReturnValue;
}
@Override
public StringBuffer printStatement(int tab, StringBuffer output){
printIndent(tab, output).append("return ");
if (this.expression != null )
this.expression.printExpression(0, output) ;
return output.append(';');
}
@Override
public void resolve(BlockScope scope) {
MethodScope methodScope = scope.methodScope();
MethodBinding methodBinding;
LambdaExpression lambda = methodScope.referenceContext instanceof LambdaExpression ? (LambdaExpression) methodScope.referenceContext : null;
TypeBinding methodType =
lambda != null ? lambda.expectedResultType() :
(methodScope.referenceContext instanceof AbstractMethodDeclaration)
? ((methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding) == null
? null
: methodBinding.returnType)
: TypeBinding.VOID;
TypeBinding expressionType;
if (this.expression != null) {
this.expression.setExpressionContext(ASSIGNMENT_CONTEXT);
this.expression.setExpectedType(methodType);
if (lambda != null && lambda.argumentsTypeElided() && this.expression instanceof CastExpression) {
this.expression.bits |= ASTNode.DisableUnnecessaryCastCheck;
}
}
if (methodType == TypeBinding.VOID) {
if (this.expression == null) {
if (lambda != null)
lambda.returnsExpression(null, TypeBinding.VOID);
return;
}
expressionType = this.expression.resolveType(scope);
if (lambda != null)
lambda.returnsExpression(this.expression, expressionType);
if (this.implicitReturn && (expressionType == TypeBinding.VOID || this.expression.statementExpression()))
return;
if (expressionType != null)
scope.problemReporter().attemptToReturnNonVoidExpression(this, expressionType);
return;
}
if (this.expression == null) {
if (lambda != null)
lambda.returnsExpression(null, methodType);
if (methodType != null) scope.problemReporter().shouldReturn(methodType, this);
return;
}
expressionType = this.expression.resolveType(scope);
if (lambda != null)
lambda.returnsExpression(this.expression, expressionType);
if (expressionType == null) return;
if (expressionType == TypeBinding.VOID) {
scope.problemReporter().attemptToReturnVoidValue(this);
return;
}
if (methodType == null)
return;
if (methodType.isProperType(true) && lambda != null) {
if (lambda.updateLocalTypesInMethod(lambda.descriptor))
methodType = lambda.expectedResultType();
}
if (TypeBinding.notEquals(methodType, expressionType))
scope.compilationUnitScope().recordTypeConversion(methodType, expressionType);
if (this.expression.isConstantValueOfTypeAssignableToType(expressionType, methodType)
|| expressionType.isCompatibleWith(methodType, scope)) {
this.expression.computeConversion(scope, methodType, expressionType);
if (expressionType.needsUncheckedConversion(methodType)) {
scope.problemReporter().unsafeTypeConversion(this.expression, expressionType, methodType);
}
if (this.expression instanceof CastExpression) {
if ((this.expression.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) {
CastExpression.checkNeedForAssignedCast(scope, methodType, (CastExpression) this.expression);
} else if (lambda != null && lambda.argumentsTypeElided() && (this.expression.bits & ASTNode.UnnecessaryCast) != 0) {
if (TypeBinding.equalsEquals(((CastExpression)this.expression).expression.resolvedType, methodType)) {
scope.problemReporter().unnecessaryCast((CastExpression)this.expression);
}
}
}
return;
} else if (isBoxingCompatible(expressionType, methodType, this.expression, scope)) {
this.expression.computeConversion(scope, methodType, expressionType);
if (this.expression instanceof CastExpression
&& (this.expression.bits & (ASTNode.UnnecessaryCast|ASTNode.DisableUnnecessaryCastCheck)) == 0) {
CastExpression.checkNeedForAssignedCast(scope, methodType, (CastExpression) this.expression);
} return;
}
if ((methodType.tagBits & TagBits.HasMissingType) == 0) {
scope.problemReporter().typeMismatchError(expressionType, methodType, this.expression, this);
}
}
@Override
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
if (this.expression != null)
this.expression.traverse(visitor, scope);
}
visitor.endVisit(this, scope);
}
}