8da806d6af02671467d3f5772986f7d2f152ee21
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / ReturnStatement.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
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.*;
17
18 public class ReturnStatement extends Statement {
19         public Expression expression;
20
21         public TypeBinding expressionType;
22         public boolean isSynchronized;
23         public AstNode[] subroutines;
24         public LocalVariableBinding saveValueVariable;
25
26 public ReturnStatement(Expression expr, int s, int e ) {
27         sourceStart = s;
28         sourceEnd = e;
29         expression = expr ;
30 }
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.
33
34         // lookup the label, this should answer the returnContext
35
36         if (expression != null) {
37                 flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo);
38         }
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;
44         while (true) {
45                 AstNode sub;
46                 if ((sub = traversedContext.subRoutine()) != null) {
47                         if (this.subroutines == null){
48                                 this.subroutines = new AstNode[maxSub];
49                         }
50                         if (subIndex == maxSub) {
51                                 System.arraycopy(this.subroutines, 0, (this.subroutines = new AstNode[maxSub *= 2]), 0, subIndex); // grow
52                         }
53                         this.subroutines[subIndex++] = sub;
54                         if (sub.cannotReturn()) {
55                                 saveValueNeeded = false;
56                                 break;
57                         }
58                 }
59                 AstNode node;
60
61                 if ((node = traversedContext.associatedNode) instanceof SynchronizedStatement) {
62                         isSynchronized = true;
63
64                 } else if (node instanceof TryStatement && hasValueToSave) {
65                                 if (this.saveValueVariable == null){ // closest subroutine secret variable is used
66                                         prepareSaveValueLocation((TryStatement)node);
67                                 }
68                                 saveValueNeeded = true;
69
70                 } else if (traversedContext instanceof InitializationFlowContext) {
71                                 currentScope.problemReporter().cannotReturnInInitializer(this);
72                                 return FlowInfo.DeadEnd;
73                 }
74
75                 // remember the initialization at this
76                 // point for dealing with blank final variables.
77                 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
78
79                 FlowContext parentContext;
80                 if ((parentContext = traversedContext.parent) == null) { // top-context
81                         break;
82                 } else {
83                         traversedContext = parentContext;
84                 }
85         }
86         // resize subroutines
87         if ((subroutines != null) && (subIndex != maxSub)) {
88                 System.arraycopy(subroutines, 0, (subroutines = new AstNode[subIndex]), 0, subIndex);
89         }
90
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;
95                 }
96         } else {
97                 this.saveValueVariable = null;
98                 if ((!isSynchronized) && (expressionType == BooleanBinding)) {
99                         this.expression.bits |= ValueForReturnMASK;
100                 }
101         }
102         return FlowInfo.DeadEnd;
103 }
104  
105 /**
106  * Retrun statement code generation
107  *
108  *   generate the finallyInvocationSequence.
109  *
110  * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
111  * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
112  */
113 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
114         if ((bits & IsReachableMASK) == 0) {
115                 return;
116         }
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);
122         }
123         
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++) {
127                         AstNode sub;
128                         if ((sub = subroutines[i]) instanceof SynchronizedStatement) {
129                                 codeStream.load(((SynchronizedStatement) sub).synchroVariable);
130                                 codeStream.monitorexit();
131                         } else {
132                                 TryStatement trySub = (TryStatement) sub;
133                                 if (trySub.subRoutineCannotReturn) {
134                                         codeStream.goto_(trySub.subRoutineStartLabel);
135                                         codeStream.recordPositionsFrom(pc, this.sourceStart);
136                                         return;
137                                 } else {
138                                         codeStream.jsr(trySub.subRoutineStartLabel);
139                                 }
140                         }
141                 }
142         }
143         if (saveValueVariable != null) codeStream.load(saveValueVariable);
144         
145         if ((expression != null) && (expression.constant != NotAConstant)) {
146                 codeStream.generateConstant(expression.constant, expression.implicitConversion);
147                 generateStoreSaveValueIfNecessary(currentScope, codeStream);            
148         }
149         // output the suitable return bytecode or wrap the value inside a descriptor for doits
150         this.generateReturnBytecode(currentScope, codeStream);
151         
152         codeStream.recordPositionsFrom(pc, this.sourceStart);
153 }
154 /**
155  * Dump the suitable return bytecode for a return statement
156  *
157  */
158 public void generateReturnBytecode(BlockScope currentScope, CodeStream codeStream) {
159
160         if (expression == null) {
161                 codeStream.return_();
162         } else {
163                 switch (expression.implicitConversion >> 4) {
164                         case T_boolean :
165                         case T_int :
166                                 codeStream.ireturn();
167                                 break;
168                         case T_float :
169                                 codeStream.freturn();
170                                 break;
171                         case T_long :
172                                 codeStream.lreturn();
173                                 break;
174                         case T_double :
175                                 codeStream.dreturn();
176                                 break;
177                         default :
178                                 codeStream.areturn();
179                 }
180         }
181 }
182 public void generateStoreSaveValueIfNecessary(BlockScope currentScope, CodeStream codeStream){
183
184         if (saveValueVariable != null) codeStream.store(saveValueVariable, false);
185 }
186 public boolean needValue(){
187         return (subroutines == null) || (saveValueVariable != null) || isSynchronized;
188 }
189 public void prepareSaveValueLocation(TryStatement targetTryStatement){
190                 
191         this.saveValueVariable = targetTryStatement.secretReturnValue;
192 }
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 
199                                 ? null 
200                                 : methodBinding.returnType)
201                         : VoidBinding;
202         if (methodType == VoidBinding) {
203                 // the expression should be null
204                 if (expression == null)
205                         return;
206                 if ((expressionType = expression.resolveType(scope)) != null)
207                         scope.problemReporter().attemptToReturnNonVoidExpression(this, expressionType);
208                 return;
209         }
210         if (expression == null) {
211                 if (methodType != null) scope.problemReporter().shouldReturn(methodType, this);
212                 return;
213         }
214         if ((expressionType = expression.resolveType(scope)) == null)
215                 return;
216
217         if (methodType != null && expression.isConstantValueOfTypeAssignableToType(expressionType, methodType)) {
218                 // dealing with constant
219                 expression.implicitWidening(methodType, expressionType);
220                 return;
221         }
222         if (expressionType == VoidBinding) {
223                 scope.problemReporter().attemptToReturnVoidValue(this);
224                 return;
225         }
226         if (methodType != null && scope.areTypesCompatible(expressionType, methodType)) {
227                 expression.implicitWidening(methodType, expressionType);
228                 return;
229         }
230         if (methodType != null){
231                 scope.problemReporter().typeMismatchErrorActualTypeExpectedType(expression, expressionType, methodType);
232         }
233 }
234 public String toString(int tab){
235
236         String s = tabString(tab) ;
237         s = s + "return "; //$NON-NLS-1$
238         if (expression != null )
239                 s = s + expression.toStringExpression() ;
240         return s;
241 }
242 public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
243         if (visitor.visit(this, scope)) {
244                 if (expression != null)
245                         expression.traverse(visitor, scope);
246         }
247         visitor.endVisit(this, scope);
248 }
249 }