1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.*;
15 import net.sourceforge.phpdt.internal.compiler.flow.*;
16 import net.sourceforge.phpdt.internal.compiler.lookup.*;
18 public class ReturnStatement extends Statement {
19 public Expression expression;
21 public TypeBinding expressionType;
22 public boolean isSynchronized;
23 public AstNode[] subroutines;
24 public LocalVariableBinding saveValueVariable;
26 public ReturnStatement(Expression expr, int s, int e ) {
31 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // here requires to generate a sequence of finally blocks invocations depending corresponding
32 // to each of the traversed try statements, so that execution will terminate properly.
34 // lookup the label, this should answer the returnContext
36 if (expression != null) {
37 flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo);
39 // compute the return sequence (running the finally blocks)
40 FlowContext traversedContext = flowContext;
41 int subIndex = 0, maxSub = 5;
42 boolean saveValueNeeded = false;
43 boolean hasValueToSave = expression != null && expression.constant == NotAConstant;
46 if ((sub = traversedContext.subRoutine()) != null) {
47 if (this.subroutines == null){
48 this.subroutines = new AstNode[maxSub];
50 if (subIndex == maxSub) {
51 System.arraycopy(this.subroutines, 0, (this.subroutines = new AstNode[maxSub *= 2]), 0, subIndex); // grow
53 this.subroutines[subIndex++] = sub;
54 if (sub.cannotReturn()) {
55 saveValueNeeded = false;
61 if ((node = traversedContext.associatedNode) instanceof SynchronizedStatement) {
62 isSynchronized = true;
64 } else if (node instanceof TryStatement && hasValueToSave) {
65 if (this.saveValueVariable == null){ // closest subroutine secret variable is used
66 prepareSaveValueLocation((TryStatement)node);
68 saveValueNeeded = true;
70 } else if (traversedContext instanceof InitializationFlowContext) {
71 currentScope.problemReporter().cannotReturnInInitializer(this);
72 return FlowInfo.DeadEnd;
75 // remember the initialization at this
76 // point for dealing with blank final variables.
77 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
79 FlowContext parentContext;
80 if ((parentContext = traversedContext.parent) == null) { // top-context
83 traversedContext = parentContext;
87 if ((subroutines != null) && (subIndex != maxSub)) {
88 System.arraycopy(subroutines, 0, (subroutines = new AstNode[subIndex]), 0, subIndex);
91 // secret local variable for return value (note that this can only occur in a real method)
92 if (saveValueNeeded) {
93 if (this.saveValueVariable != null) {
94 this.saveValueVariable.used = true;
97 this.saveValueVariable = null;
98 if ((!isSynchronized) && (expressionType == BooleanBinding)) {
99 this.expression.bits |= ValueForReturnMASK;
102 return FlowInfo.DeadEnd;
106 * Retrun statement code generation
108 * generate the finallyInvocationSequence.
110 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
111 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
113 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
114 if ((bits & IsReachableMASK) == 0) {
117 int pc = codeStream.position;
118 // generate the expression
119 if ((expression != null) && (expression.constant == NotAConstant)) {
120 expression.generateCode(currentScope, codeStream, needValue()); // no value needed if non-returning subroutine
121 generateStoreSaveValueIfNecessary(currentScope, codeStream);
124 // generation of code responsible for invoking the finally blocks in sequence
125 if (subroutines != null) {
126 for (int i = 0, max = subroutines.length; i < max; i++) {
128 if ((sub = subroutines[i]) instanceof SynchronizedStatement) {
129 codeStream.load(((SynchronizedStatement) sub).synchroVariable);
130 codeStream.monitorexit();
132 TryStatement trySub = (TryStatement) sub;
133 if (trySub.subRoutineCannotReturn) {
134 codeStream.goto_(trySub.subRoutineStartLabel);
135 codeStream.recordPositionsFrom(pc, this.sourceStart);
138 codeStream.jsr(trySub.subRoutineStartLabel);
143 if (saveValueVariable != null) codeStream.load(saveValueVariable);
145 if ((expression != null) && (expression.constant != NotAConstant)) {
146 codeStream.generateConstant(expression.constant, expression.implicitConversion);
147 generateStoreSaveValueIfNecessary(currentScope, codeStream);
149 // output the suitable return bytecode or wrap the value inside a descriptor for doits
150 this.generateReturnBytecode(currentScope, codeStream);
152 codeStream.recordPositionsFrom(pc, this.sourceStart);
155 * Dump the suitable return bytecode for a return statement
158 public void generateReturnBytecode(BlockScope currentScope, CodeStream codeStream) {
160 if (expression == null) {
161 codeStream.return_();
163 switch (expression.implicitConversion >> 4) {
166 codeStream.ireturn();
169 codeStream.freturn();
172 codeStream.lreturn();
175 codeStream.dreturn();
178 codeStream.areturn();
182 public void generateStoreSaveValueIfNecessary(BlockScope currentScope, CodeStream codeStream){
184 if (saveValueVariable != null) codeStream.store(saveValueVariable, false);
186 public boolean needValue(){
187 return (subroutines == null) || (saveValueVariable != null) || isSynchronized;
189 public void prepareSaveValueLocation(TryStatement targetTryStatement){
191 this.saveValueVariable = targetTryStatement.secretReturnValue;
193 public void resolve(BlockScope scope) {
194 MethodScope methodScope = scope.methodScope();
195 MethodBinding methodBinding;
196 TypeBinding methodType =
197 (methodScope.referenceContext instanceof AbstractMethodDeclaration)
198 ? ((methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding) == null
200 : methodBinding.returnType)
202 if (methodType == VoidBinding) {
203 // the expression should be null
204 if (expression == null)
206 if ((expressionType = expression.resolveType(scope)) != null)
207 scope.problemReporter().attemptToReturnNonVoidExpression(this, expressionType);
210 if (expression == null) {
211 if (methodType != null) scope.problemReporter().shouldReturn(methodType, this);
214 if ((expressionType = expression.resolveType(scope)) == null)
217 if (methodType != null && expression.isConstantValueOfTypeAssignableToType(expressionType, methodType)) {
218 // dealing with constant
219 expression.implicitWidening(methodType, expressionType);
222 if (expressionType == VoidBinding) {
223 scope.problemReporter().attemptToReturnVoidValue(this);
226 if (methodType != null && scope.areTypesCompatible(expressionType, methodType)) {
227 expression.implicitWidening(methodType, expressionType);
230 if (methodType != null){
231 scope.problemReporter().typeMismatchErrorActualTypeExpectedType(expression, expressionType, methodType);
234 public String toString(int tab){
236 String s = tabString(tab) ;
237 s = s + "return "; //$NON-NLS-1$
238 if (expression != null )
239 s = s + expression.toStringExpression() ;
242 public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
243 if (visitor.visit(this, scope)) {
244 if (expression != null)
245 expression.traverse(visitor, scope);
247 visitor.endVisit(this, scope);