001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
011:
012: import org.eclipse.jdt.internal.compiler.ASTVisitor;
013: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
014: import org.eclipse.jdt.internal.compiler.codegen.*;
015: import org.eclipse.jdt.internal.compiler.flow.*;
016: import org.eclipse.jdt.internal.compiler.impl.Constant;
017: import org.eclipse.jdt.internal.compiler.lookup.*;
018:
019: public class ReturnStatement extends Statement {
020:
021: public Expression expression;
022: public SubRoutineStatement[] subroutines;
023: public LocalVariableBinding saveValueVariable;
024: public int initStateIndex = -1;
025:
026: public ReturnStatement(Expression expression, int sourceStart,
027: int sourceEnd) {
028: this .sourceStart = sourceStart;
029: this .sourceEnd = sourceEnd;
030: this .expression = expression;
031: }
032:
033: public FlowInfo analyseCode(BlockScope currentScope,
034: FlowContext flowContext, FlowInfo flowInfo) { // here requires to generate a sequence of finally blocks invocations depending corresponding
035: // to each of the traversed try statements, so that execution will terminate properly.
036:
037: // lookup the label, this should answer the returnContext
038:
039: if (this .expression != null) {
040: flowInfo = this .expression.analyseCode(currentScope,
041: flowContext, flowInfo);
042: }
043: this .initStateIndex = currentScope.methodScope()
044: .recordInitializationStates(flowInfo);
045: // compute the return sequence (running the finally blocks)
046: FlowContext traversedContext = flowContext;
047: int subCount = 0;
048: boolean saveValueNeeded = false;
049: boolean hasValueToSave = needValueStore();
050: do {
051: SubRoutineStatement sub;
052: if ((sub = traversedContext.subroutine()) != null) {
053: if (this .subroutines == null) {
054: this .subroutines = new SubRoutineStatement[5];
055: }
056: if (subCount == this .subroutines.length) {
057: System
058: .arraycopy(
059: this .subroutines,
060: 0,
061: (this .subroutines = new SubRoutineStatement[subCount * 2]),
062: 0, subCount); // grow
063: }
064: this .subroutines[subCount++] = sub;
065: if (sub.isSubRoutineEscaping()) {
066: saveValueNeeded = false;
067: this .bits |= ASTNode.IsAnySubRoutineEscaping;
068: break;
069: }
070: }
071: traversedContext.recordReturnFrom(flowInfo
072: .unconditionalInits());
073:
074: if (traversedContext instanceof InsideSubRoutineFlowContext) {
075: ASTNode node = traversedContext.associatedNode;
076: if (node instanceof SynchronizedStatement) {
077: this .bits |= ASTNode.IsSynchronized;
078: } else if (node instanceof TryStatement) {
079: TryStatement tryStatement = (TryStatement) node;
080: flowInfo
081: .addInitializationsFrom(tryStatement.subRoutineInits); // collect inits
082: if (hasValueToSave) {
083: if (this .saveValueVariable == null) { // closest subroutine secret variable is used
084: prepareSaveValueLocation(tryStatement);
085: }
086: saveValueNeeded = true;
087: }
088: }
089: } else if (traversedContext instanceof InitializationFlowContext) {
090: currentScope.problemReporter()
091: .cannotReturnInInitializer(this );
092: return FlowInfo.DEAD_END;
093: }
094: } while ((traversedContext = traversedContext.parent) != null);
095:
096: // resize subroutines
097: if ((this .subroutines != null)
098: && (subCount != this .subroutines.length)) {
099: System
100: .arraycopy(
101: this .subroutines,
102: 0,
103: (this .subroutines = new SubRoutineStatement[subCount]),
104: 0, subCount);
105: }
106:
107: // secret local variable for return value (note that this can only occur in a real method)
108: if (saveValueNeeded) {
109: if (this .saveValueVariable != null) {
110: this .saveValueVariable.useFlag = LocalVariableBinding.USED;
111: }
112: } else {
113: this .saveValueVariable = null;
114: if (((this .bits & ASTNode.IsSynchronized) == 0)
115: && this .expression != null
116: && this .expression.resolvedType == TypeBinding.BOOLEAN) {
117: this .expression.bits |= ASTNode.IsReturnedValue;
118: }
119: }
120: return FlowInfo.DEAD_END;
121: }
122:
123: /**
124: * Retrun statement code generation
125: *
126: * generate the finallyInvocationSequence.
127: *
128: * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
129: * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
130: */
131: public void generateCode(BlockScope currentScope,
132: CodeStream codeStream) {
133: if ((this .bits & ASTNode.IsReachable) == 0) {
134: return;
135: }
136: int pc = codeStream.position;
137: boolean alreadyGeneratedExpression = false;
138: // generate the expression
139: if (needValueStore()) {
140: alreadyGeneratedExpression = true;
141: this .expression.generateCode(currentScope, codeStream,
142: needValue()); // no value needed if non-returning subroutine
143: generateStoreSaveValueIfNecessary(codeStream);
144: }
145:
146: // generation of code responsible for invoking the finally blocks in sequence
147: if (this .subroutines != null) {
148: Object reusableJSRTarget = this .expression == null ? (Object) TypeBinding.VOID
149: : this .expression.reusableJSRTarget();
150: for (int i = 0, max = this .subroutines.length; i < max; i++) {
151: SubRoutineStatement sub = this .subroutines[i];
152: boolean didEscape = sub.generateSubRoutineInvocation(
153: currentScope, codeStream, reusableJSRTarget,
154: this .initStateIndex, this .saveValueVariable);
155: if (didEscape) {
156: codeStream
157: .recordPositionsFrom(pc, this .sourceStart);
158: SubRoutineStatement.reenterAllExceptionHandlers(
159: this .subroutines, i, codeStream);
160: return;
161: }
162: }
163: }
164: if (this .saveValueVariable != null) {
165: codeStream.addVariable(this .saveValueVariable);
166: codeStream.load(this .saveValueVariable);
167: }
168: if (this .expression != null && !alreadyGeneratedExpression) {
169: this .expression
170: .generateCode(currentScope, codeStream, true);
171: generateStoreSaveValueIfNecessary(codeStream);
172: }
173: // output the suitable return bytecode or wrap the value inside a descriptor for doits
174: this .generateReturnBytecode(codeStream);
175: if (this .saveValueVariable != null) {
176: codeStream.removeVariable(this .saveValueVariable);
177: }
178: if (this .initStateIndex != -1) {
179: codeStream.removeNotDefinitelyAssignedVariables(
180: currentScope, this .initStateIndex);
181: codeStream.addDefinitelyAssignedVariables(currentScope,
182: this .initStateIndex);
183: }
184: codeStream.recordPositionsFrom(pc, this .sourceStart);
185: SubRoutineStatement.reenterAllExceptionHandlers(
186: this .subroutines, -1, codeStream);
187: }
188:
189: /**
190: * Dump the suitable return bytecode for a return statement
191: *
192: */
193: public void generateReturnBytecode(CodeStream codeStream) {
194: codeStream.generateReturnBytecode(this .expression);
195: }
196:
197: public void generateStoreSaveValueIfNecessary(CodeStream codeStream) {
198: if (this .saveValueVariable != null) {
199: codeStream.store(this .saveValueVariable, false);
200: }
201: }
202:
203: private boolean needValueStore() {
204: return this .expression != null
205: && (this .expression.constant == Constant.NotAConstant || (this .expression.implicitConversion & TypeIds.BOXING) != 0)
206: && !(this .expression instanceof NullLiteral);
207: }
208:
209: public boolean needValue() {
210: return this .saveValueVariable != null
211: || (this .bits & ASTNode.IsSynchronized) != 0
212: || ((this .bits & ASTNode.IsAnySubRoutineEscaping) == 0);
213: }
214:
215: public void prepareSaveValueLocation(TryStatement targetTryStatement) {
216: this .saveValueVariable = targetTryStatement.secretReturnValue;
217: }
218:
219: public StringBuffer printStatement(int tab, StringBuffer output) {
220: printIndent(tab, output).append("return "); //$NON-NLS-1$
221: if (this .expression != null)
222: this .expression.printExpression(0, output);
223: return output.append(';');
224: }
225:
226: public void resolve(BlockScope scope) {
227: MethodScope methodScope = scope.methodScope();
228: MethodBinding methodBinding;
229: TypeBinding methodType = (methodScope.referenceContext instanceof AbstractMethodDeclaration) ? ((methodBinding = ((AbstractMethodDeclaration) methodScope.referenceContext).binding) == null ? null
230: : methodBinding.returnType)
231: : TypeBinding.VOID;
232: TypeBinding expressionType;
233: if (methodType == TypeBinding.VOID) {
234: // the expression should be null
235: if (this .expression == null)
236: return;
237: if ((expressionType = this .expression.resolveType(scope)) != null)
238: scope.problemReporter()
239: .attemptToReturnNonVoidExpression(this ,
240: expressionType);
241: return;
242: }
243: if (this .expression == null) {
244: if (methodType != null)
245: scope.problemReporter().shouldReturn(methodType, this );
246: return;
247: }
248: this .expression.setExpectedType(methodType); // needed in case of generic method invocation
249: if ((expressionType = this .expression.resolveType(scope)) == null)
250: return;
251: if (expressionType == TypeBinding.VOID) {
252: scope.problemReporter().attemptToReturnVoidValue(this );
253: return;
254: }
255: if (methodType == null)
256: return;
257:
258: if (methodType != expressionType) // must call before computeConversion() and typeMismatchError()
259: scope.compilationUnitScope().recordTypeConversion(
260: methodType, expressionType);
261: if (this .expression.isConstantValueOfTypeAssignableToType(
262: expressionType, methodType)
263: || expressionType.isCompatibleWith(methodType)) {
264:
265: this .expression.computeConversion(scope, methodType,
266: expressionType);
267: if (expressionType.needsUncheckedConversion(methodType)) {
268: scope.problemReporter().unsafeTypeConversion(
269: this .expression, expressionType, methodType);
270: }
271: if (this .expression instanceof CastExpression
272: && (this .expression.bits & (ASTNode.UnnecessaryCast | ASTNode.DisableUnnecessaryCastCheck)) == 0) {
273: CastExpression.checkNeedForAssignedCast(scope,
274: methodType, (CastExpression) this .expression);
275: }
276: return;
277: } else if (scope.isBoxingCompatibleWith(expressionType,
278: methodType)
279: || (expressionType.isBaseType() // narrowing then boxing ?
280: && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5 // autoboxing
281: && !methodType.isBaseType() && this .expression
282: .isConstantValueOfTypeAssignableToType(
283: expressionType, scope.environment()
284: .computeBoxingType(methodType)))) {
285: this .expression.computeConversion(scope, methodType,
286: expressionType);
287: if (this .expression instanceof CastExpression
288: && (this .expression.bits & (ASTNode.UnnecessaryCast | ASTNode.DisableUnnecessaryCastCheck)) == 0) {
289: CastExpression.checkNeedForAssignedCast(scope,
290: methodType, (CastExpression) this .expression);
291: }
292: return;
293: }
294: scope.problemReporter().typeMismatchError(expressionType,
295: methodType, this .expression);
296: }
297:
298: public void traverse(ASTVisitor visitor, BlockScope scope) {
299: if (visitor.visit(this, scope)) {
300: if (this.expression != null)
301: this.expression.traverse(visitor, scope);
302: }
303: visitor.endVisit(this, scope);
304: }
305: }
|