added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / ReturnStatement.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.codegen.*;
15 import org.eclipse.jdt.internal.compiler.flow.*;
16 import org.eclipse.jdt.internal.compiler.lookup.*;
17
18 public class ReturnStatement extends Statement {
19                 
20         public Expression expression;
21         public boolean isSynchronized;
22         public SubRoutineStatement[] subroutines;
23         public boolean isAnySubRoutineEscaping = false;
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                 do {
45                         SubRoutineStatement sub;
46                         if ((sub = traversedContext.subRoutine()) != null) {
47                                 if (this.subroutines == null){
48                                         this.subroutines = new SubRoutineStatement[maxSub];
49                                 }
50                                 if (subIndex == maxSub) {
51                                         System.arraycopy(this.subroutines, 0, (this.subroutines = new SubRoutineStatement[maxSub *= 2]), 0, subIndex); // grow
52                                 }
53                                 this.subroutines[subIndex++] = sub;
54                                 if (sub.isSubRoutineEscaping()) {
55                                         saveValueNeeded = false;
56                                         isAnySubRoutineEscaping = true;
57                                         break;
58                                 }
59                         }
60                         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
61         
62                         ASTNode node;
63                         if ((node = traversedContext.associatedNode) instanceof SynchronizedStatement) {
64                                 isSynchronized = true;
65         
66                         } else if (node instanceof TryStatement) {
67                                 TryStatement tryStatement = (TryStatement) node;
68                                 flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits
69                                 if (hasValueToSave) {
70                                         if (this.saveValueVariable == null){ // closest subroutine secret variable is used
71                                                 prepareSaveValueLocation(tryStatement);
72                                         }
73                                         saveValueNeeded = true;
74                                 }
75         
76                         } else if (traversedContext instanceof InitializationFlowContext) {
77                                         currentScope.problemReporter().cannotReturnInInitializer(this);
78                                         return FlowInfo.DEAD_END;
79                         }
80                 } while ((traversedContext = traversedContext.parent) != null);
81                 
82                 // resize subroutines
83                 if ((subroutines != null) && (subIndex != maxSub)) {
84                         System.arraycopy(subroutines, 0, (subroutines = new SubRoutineStatement[subIndex]), 0, subIndex);
85                 }
86         
87                 // secret local variable for return value (note that this can only occur in a real method)
88                 if (saveValueNeeded) {
89                         if (this.saveValueVariable != null) {
90                                 this.saveValueVariable.useFlag = LocalVariableBinding.USED;
91                         }
92                 } else {
93                         this.saveValueVariable = null;
94                         if (!isSynchronized && this.expression != null && this.expression.resolvedType == BooleanBinding) {
95                                 this.expression.bits |= ValueForReturnMASK;
96                         }
97                 }
98                 return FlowInfo.DEAD_END;
99         }
100          
101         /**
102          * Retrun statement code generation
103          *
104          *   generate the finallyInvocationSequence.
105          *
106          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
107          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
108          */
109         public void generateCode(BlockScope currentScope, CodeStream codeStream) {
110                 if ((bits & IsReachableMASK) == 0) {
111                         return;
112                 }
113                 int pc = codeStream.position;
114                 // generate the expression
115                 if ((expression != null) && (expression.constant == NotAConstant)) {
116                         expression.generateCode(currentScope, codeStream, needValue()); // no value needed if non-returning subroutine
117                         generateStoreSaveValueIfNecessary(codeStream);
118                 }
119                 
120                 // generation of code responsible for invoking the finally blocks in sequence
121                 if (subroutines != null) {
122                         for (int i = 0, max = subroutines.length; i < max; i++) {
123                                 SubRoutineStatement sub = subroutines[i];
124                                 sub.generateSubRoutineInvocation(currentScope, codeStream);
125                                 if (sub.isSubRoutineEscaping()) {
126                                                 codeStream.recordPositionsFrom(pc, this.sourceStart);
127                                                 SubRoutineStatement.reenterExceptionHandlers(subroutines, i, codeStream);
128                                                 return;
129                                 }
130                                 sub.exitAnyExceptionHandler();
131                         }
132                 }
133                 if (saveValueVariable != null) codeStream.load(saveValueVariable);
134                 
135                 if ((expression != null) && (expression.constant != NotAConstant)) {
136                         codeStream.generateConstant(expression.constant, expression.implicitConversion);
137                         generateStoreSaveValueIfNecessary(codeStream);          
138                 }
139                 // output the suitable return bytecode or wrap the value inside a descriptor for doits
140                 this.generateReturnBytecode(codeStream);
141                 codeStream.recordPositionsFrom(pc, this.sourceStart);
142                 SubRoutineStatement.reenterExceptionHandlers(subroutines, -1, codeStream);
143         }
144         /**
145          * Dump the suitable return bytecode for a return statement
146          *
147          */
148         public void generateReturnBytecode(CodeStream codeStream) {
149         
150                 if (expression == null) {
151                         codeStream.return_();
152                 } else {
153                         final int implicitConversion = expression.implicitConversion;
154                         if ((implicitConversion & BOXING) != 0) {
155                                 codeStream.areturn();
156                                 return;
157                         }
158                         int runtimeType = (implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4;
159                         switch (runtimeType) {
160                                 case T_boolean :
161                                 case T_int :
162                                         codeStream.ireturn();
163                                         break;
164                                 case T_float :
165                                         codeStream.freturn();
166                                         break;
167                                 case T_long :
168                                         codeStream.lreturn();
169                                         break;
170                                 case T_double :
171                                         codeStream.dreturn();
172                                         break;
173                                 default :
174                                         codeStream.areturn();
175                         }
176                 }
177         }
178         public void generateStoreSaveValueIfNecessary(CodeStream codeStream){
179                 if (saveValueVariable != null) codeStream.store(saveValueVariable, false);
180         }
181         public boolean needValue(){
182                 return (subroutines == null) || (saveValueVariable != null) || isSynchronized;
183         }
184         public void prepareSaveValueLocation(TryStatement targetTryStatement){
185                         
186                 this.saveValueVariable = targetTryStatement.secretReturnValue;
187         }
188         public StringBuffer printStatement(int tab, StringBuffer output){
189         
190                 printIndent(tab, output).append("return "); //$NON-NLS-1$
191                 if (expression != null )
192                         expression.printExpression(0, output) ;
193                 return output.append(';');
194         }
195         
196         public void resolve(BlockScope scope) {
197                 
198                 MethodScope methodScope = scope.methodScope();
199                 MethodBinding methodBinding;
200                 TypeBinding methodType =
201                         (methodScope.referenceContext instanceof AbstractMethodDeclaration)
202                                 ? ((methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding) == null 
203                                         ? null 
204                                         : methodBinding.returnType)
205                                 : VoidBinding;
206                 TypeBinding expressionType;
207                 if (methodType == VoidBinding) {
208                         // the expression should be null
209                         if (expression == null)
210                                 return;
211                         if ((expressionType = expression.resolveType(scope)) != null)
212                                 scope.problemReporter().attemptToReturnNonVoidExpression(this, expressionType);
213                         return;
214                 }
215                 if (expression == null) {
216                         if (methodType != null) scope.problemReporter().shouldReturn(methodType, this);
217                         return;
218                 }
219                 expression.setExpectedType(methodType); // needed in case of generic method invocation
220                 if ((expressionType = expression.resolveType(scope)) == null) return;
221                 if (expressionType == VoidBinding) {
222                         scope.problemReporter().attemptToReturnVoidValue(this);
223                         return;
224                 }
225                 if (methodType == null) 
226                         return;
227         
228                 if (methodType != expressionType) // must call before computeConversion() and typeMismatchError()
229                         scope.compilationUnitScope().recordTypeConversion(methodType, expressionType);
230                 if (expression.isConstantValueOfTypeAssignableToType(expressionType, methodType)
231                                 || expressionType.isCompatibleWith(methodType)) {
232
233                         expression.computeConversion(scope, methodType, expressionType);
234                         if (expressionType.needsUncheckedConversion(methodType)) {
235                             scope.problemReporter().unsafeRawConversion(this.expression, expressionType, methodType);
236                         }
237                         return;
238                 } else if (scope.isBoxingCompatibleWith(expressionType, methodType)) {
239                         expression.computeConversion(scope, methodType, expressionType);
240                         return;
241                 }
242                 scope.problemReporter().typeMismatchError(expressionType, methodType, expression);
243         }
244         public void traverse(ASTVisitor visitor, BlockScope scope) {
245                 if (visitor.visit(this, scope)) {
246                         if (expression != null)
247                                 expression.traverse(visitor, scope);
248                 }
249                 visitor.endVisit(this, scope);
250         }
251 }