/******************************************************************************* * Copyright (c) 2000, 2011 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 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.compiler.ast; import org.eclipse.wst.jsdt.core.ast.IASTNode; import org.eclipse.wst.jsdt.core.ast.IExpression; import org.eclipse.wst.jsdt.core.ast.IReturnStatement; import org.eclipse.wst.jsdt.internal.compiler.ASTVisitor; import org.eclipse.wst.jsdt.internal.compiler.flow.FlowContext; import org.eclipse.wst.jsdt.internal.compiler.flow.FlowInfo; import org.eclipse.wst.jsdt.internal.compiler.flow.InitializationFlowContext; import org.eclipse.wst.jsdt.internal.compiler.flow.InsideSubRoutineFlowContext; import org.eclipse.wst.jsdt.internal.compiler.impl.Constant; import org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope; import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.MethodScope; import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding; public class ReturnStatement extends Statement implements IReturnStatement { public Expression expression; public SubRoutineStatement[] subroutines; public LocalVariableBinding saveValueVariable; public int initStateIndex = -1; public ReturnStatement(Expression expression, int sourceStart, int sourceEnd) { this.sourceStart = sourceStart; this.sourceEnd = sourceEnd; this.expression = expression ; } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { if (this.expression != null) { flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo); } // compute the return sequence (running the finally blocks) FlowContext traversedContext = flowContext; int subCount = 0; boolean saveValueNeeded = false; boolean hasValueToSave = this.expression != null && this.expression.constant == Constant.NotAConstant && !(this.expression instanceof NullLiteral); 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); // grow } this.subroutines[subCount++] = sub; if (sub.isSubRoutineEscaping()) { saveValueNeeded = false; this.bits |= ASTNode.IsAnySubRoutineEscaping; break; } } traversedContext.recordReturnFrom(flowInfo.unconditionalInits()); if (traversedContext instanceof InsideSubRoutineFlowContext) { ASTNode node = traversedContext.associatedNode; if (node instanceof TryStatement) { TryStatement tryStatement = (TryStatement) node; flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits if (hasValueToSave) { if (this.saveValueVariable == null){ // closest subroutine secret variable is used prepareSaveValueLocation(tryStatement); } saveValueNeeded = true; } } } else if (traversedContext instanceof InitializationFlowContext) { currentScope.problemReporter().cannotReturnOutsideFunction(this); return FlowInfo.DEAD_END; } } while ((traversedContext = traversedContext.parent) != null); // resize subroutines if ((this.subroutines != null) && (subCount != this.subroutines.length)) { System.arraycopy(this.subroutines, 0, (this.subroutines = new SubRoutineStatement[subCount]), 0, subCount); } // secret local variable for return value (note that this can only occur in a real method) if (saveValueNeeded) { if (this.saveValueVariable != null) { this.saveValueVariable.useFlag = LocalVariableBinding.USED; } } else { this.saveValueVariable = null; if ( this.expression != null && this.expression.resolvedType == TypeBinding.BOOLEAN) { this.expression.bits |= ASTNode.IsReturnedValue; } } return FlowInfo.DEAD_END; } public boolean needValue() { return this.saveValueVariable != null || ((this.bits & ASTNode.IsAnySubRoutineEscaping) == 0); } public void prepareSaveValueLocation(TryStatement targetTryStatement){ this.saveValueVariable = targetTryStatement.secretReturnValue; } public StringBuffer printStatement(int tab, StringBuffer output){ printIndent(tab, output).append("return "); //$NON-NLS-1$ if (this.expression != null ) this.expression.printExpression(0, output) ; return output.append(';'); } public void resolve(BlockScope scope) { MethodScope methodScope = scope.methodScope(); if(methodScope==null) { /* return statement outside of a method */ scope.problemReporter().cannotReturnOutsideFunction(this); return; } MethodBinding methodBinding = null; TypeBinding methodType = (methodScope.referenceContext instanceof AbstractMethodDeclaration) ? ((methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding) == null ? null : methodBinding.returnType) : TypeBinding.ANY; TypeBinding expressionType; if (this.expression == null) { if (methodType != null && !methodType.isAnyType()) scope.problemReporter().shouldReturn(methodType, this); return; } this.expression.setExpectedType(methodType); // needed in case of generic method invocation if ((expressionType = this.expression.resolveType(scope)) == null) return; if (methodType == null) return; if (methodType != expressionType) // must call before computeConversion() and typeMismatchError() scope.compilationUnitScope().recordTypeConversion(methodType, expressionType); if (this.expression.isConstantValueOfTypeAssignableToType(expressionType, methodType) || expressionType.isCompatibleWith(methodType)) { return; } if(methodBinding != null && !methodBinding.isConstructor()) scope.problemReporter().typeMismatchError(expressionType, methodType, this.expression); } 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); } public int getASTType() { return IASTNode.RETURN_STATEMENT; } public IExpression getExpression() { return this.expression; } }