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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.flow;
13 import org.eclipse.jdt.core.compiler.CharOperation;
14 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
15 import org.eclipse.jdt.internal.compiler.ast.ASTNode;
16 import org.eclipse.jdt.internal.compiler.ast.Expression;
17 import org.eclipse.jdt.internal.compiler.ast.Reference;
18 import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
19 import org.eclipse.jdt.internal.compiler.ast.TryStatement;
20 import org.eclipse.jdt.internal.compiler.codegen.Label;
21 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
22 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
23 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
24 import org.eclipse.jdt.internal.compiler.lookup.Scope;
25 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
26 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
27 import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
30 * Reflects the context of code analysis, keeping track of enclosing
31 * try statements, exception handlers, etc...
33 public class FlowContext implements TypeConstants {
35 public ASTNode associatedNode;
36 public FlowContext parent;
38 public final static FlowContext NotContinuableContext = new FlowContext(null, null);
40 public FlowContext(FlowContext parent, ASTNode associatedNode) {
43 this.associatedNode = associatedNode;
46 public Label breakLabel() {
51 public void checkExceptionHandlers(
52 TypeBinding[] raisedExceptions,
57 // check that all the argument exception types are handled
58 // JDK Compatible implementation - when an exception type is thrown,
59 // all related catch blocks are marked as reachable... instead of those only
60 // until the point where it is safely handled (Smarter - see comment at the end)
61 int remainingCount; // counting the number of remaining unhandled exceptions
62 int raisedCount; // total number of exceptions raised
63 if ((raisedExceptions == null)
64 || ((raisedCount = raisedExceptions.length) == 0))
66 remainingCount = raisedCount;
68 // duplicate the array of raised exceptions since it will be updated
69 // (null replaces any handled exception)
73 (raisedExceptions = new TypeBinding[raisedCount]),
76 FlowContext traversedContext = this;
78 while (traversedContext != null) {
79 SubRoutineStatement sub;
80 if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
81 // traversing a non-returning subroutine means that all unhandled
82 // exceptions will actually never get sent...
85 // filter exceptions that are locally caught from the innermost enclosing
86 // try statement to the outermost ones.
87 if (traversedContext instanceof ExceptionHandlingFlowContext) {
88 ExceptionHandlingFlowContext exceptionContext =
89 (ExceptionHandlingFlowContext) traversedContext;
90 ReferenceBinding[] caughtExceptions;
91 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
92 int caughtCount = caughtExceptions.length;
93 boolean[] locallyCaught = new boolean[raisedCount]; // at most
95 for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
96 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
97 for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
98 TypeBinding raisedException;
99 if ((raisedException = raisedExceptions[raisedIndex]) != null) {
100 int state = caughtException == null
101 ? EqualOrMoreSpecific /* any exception */
102 : Scope.compareTypes(raisedException, caughtException);
104 case EqualOrMoreSpecific :
105 exceptionContext.recordHandlingException(
107 flowInfo.unconditionalInits(),
110 locallyCaught[raisedIndex]);
111 // was already definitely caught ?
112 if (!locallyCaught[raisedIndex]) {
113 locallyCaught[raisedIndex] = true;
114 // remember that this exception has been definitely caught
119 exceptionContext.recordHandlingException(
121 flowInfo.unconditionalInits(),
125 // was not caught already per construction
130 // remove locally caught exceptions from the remaining ones
131 for (int i = 0; i < raisedCount; i++) {
132 if (locallyCaught[i]) {
133 raisedExceptions[i] = null; // removed from the remaining ones.
137 // method treatment for unchecked exceptions
138 if (exceptionContext.isMethodContext) {
139 for (int i = 0; i < raisedCount; i++) {
140 TypeBinding raisedException;
141 if ((raisedException = raisedExceptions[i]) != null) {
142 if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
143 || raisedException.isCompatibleWith(scope.getJavaLangError())) {
145 raisedExceptions[i] = null;
149 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
150 // clause will be fixed up later as per JLS 8.6).
151 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
152 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
153 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
155 for (int i = 0; i < raisedCount; i++) {
156 TypeBinding raisedException;
157 if ((raisedException = raisedExceptions[i]) != null) {
158 exceptionContext.mergeUnhandledException(raisedException);
161 return; // no need to complain, will fix up constructor exceptions
164 break; // not handled anywhere, thus jump to error handling
167 if (remainingCount == 0)
170 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
171 if (traversedContext.associatedNode instanceof TryStatement){
172 TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
173 flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits);
175 traversedContext = traversedContext.parent;
177 // if reaches this point, then there are some remaining unhandled exception types.
178 nextReport: for (int i = 0; i < raisedCount; i++) {
179 TypeBinding exception;
180 if ((exception = raisedExceptions[i]) != null) {
181 // only one complaint if same exception declared to be thrown more than once
182 for (int j = 0; j < i; j++) {
183 if (raisedExceptions[j] == exception) continue nextReport; // already reported
185 scope.problemReporter().unhandledException(exception, location);
190 public void checkExceptionHandlers(
191 TypeBinding raisedException,
196 // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
197 // check that all the argument exception types are handled
198 // JDK Compatible implementation - when an exception type is thrown,
199 // all related catch blocks are marked as reachable... instead of those only
200 // until the point where it is safely handled (Smarter - see comment at the end)
201 FlowContext traversedContext = this;
202 while (traversedContext != null) {
203 SubRoutineStatement sub;
204 if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
205 // traversing a non-returning subroutine means that all unhandled
206 // exceptions will actually never get sent...
210 // filter exceptions that are locally caught from the innermost enclosing
211 // try statement to the outermost ones.
212 if (traversedContext instanceof ExceptionHandlingFlowContext) {
213 ExceptionHandlingFlowContext exceptionContext =
214 (ExceptionHandlingFlowContext) traversedContext;
215 ReferenceBinding[] caughtExceptions;
216 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
217 boolean definitelyCaught = false;
218 for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
219 caughtIndex < caughtCount;
221 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
222 int state = caughtException == null
223 ? EqualOrMoreSpecific /* any exception */
224 : Scope.compareTypes(raisedException, caughtException);
226 case EqualOrMoreSpecific :
227 exceptionContext.recordHandlingException(
229 flowInfo.unconditionalInits(),
233 // was it already definitely caught ?
234 definitelyCaught = true;
237 exceptionContext.recordHandlingException(
239 flowInfo.unconditionalInits(),
243 // was not caught already per construction
246 if (definitelyCaught)
249 // method treatment for unchecked exceptions
250 if (exceptionContext.isMethodContext) {
251 if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
252 || raisedException.isCompatibleWith(scope.getJavaLangError()))
255 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
256 // clause will be fixed up later as per JLS 8.6).
257 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
258 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
259 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
261 exceptionContext.mergeUnhandledException(raisedException);
262 return; // no need to complain, will fix up constructor exceptions
265 break; // not handled anywhere, thus jump to error handling
269 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
270 if (traversedContext.associatedNode instanceof TryStatement){
271 TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
272 flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits);
274 traversedContext = traversedContext.parent;
276 // if reaches this point, then there are some remaining unhandled exception types.
277 scope.problemReporter().unhandledException(raisedException, location);
280 public Label continueLabel() {
286 * lookup through break labels
288 public FlowContext getTargetContextForBreakLabel(char[] labelName) {
290 FlowContext current = this, lastNonReturningSubRoutine = null;
291 while (current != null) {
292 if (current.isNonReturningContext()) {
293 lastNonReturningSubRoutine = current;
295 char[] currentLabelName;
296 if (((currentLabelName = current.labelName()) != null)
297 && CharOperation.equals(currentLabelName, labelName)) {
298 if (lastNonReturningSubRoutine == null)
300 return lastNonReturningSubRoutine;
302 current = current.parent;
309 * lookup through continue labels
311 public FlowContext getTargetContextForContinueLabel(char[] labelName) {
313 FlowContext current = this;
314 FlowContext lastContinuable = null;
315 FlowContext lastNonReturningSubRoutine = null;
317 while (current != null) {
318 if (current.isNonReturningContext()) {
319 lastNonReturningSubRoutine = current;
321 if (current.isContinuable()) {
322 lastContinuable = current;
326 char[] currentLabelName;
327 if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
329 // matching label found
330 if ((lastContinuable != null)
331 && (current.associatedNode.concreteStatement() == lastContinuable.associatedNode)) {
333 if (lastNonReturningSubRoutine == null) return lastContinuable;
334 return lastNonReturningSubRoutine;
336 // label is found, but not a continuable location
337 return NotContinuableContext;
339 current = current.parent;
346 * lookup a default break through breakable locations
348 public FlowContext getTargetContextForDefaultBreak() {
350 FlowContext current = this, lastNonReturningSubRoutine = null;
351 while (current != null) {
352 if (current.isNonReturningContext()) {
353 lastNonReturningSubRoutine = current;
355 if (current.isBreakable() && current.labelName() == null) {
356 if (lastNonReturningSubRoutine == null) return current;
357 return lastNonReturningSubRoutine;
359 current = current.parent;
366 * lookup a default continue amongst continuable locations
368 public FlowContext getTargetContextForDefaultContinue() {
370 FlowContext current = this, lastNonReturningSubRoutine = null;
371 while (current != null) {
372 if (current.isNonReturningContext()) {
373 lastNonReturningSubRoutine = current;
375 if (current.isContinuable()) {
376 if (lastNonReturningSubRoutine == null)
378 return lastNonReturningSubRoutine;
380 current = current.parent;
386 public String individualToString() {
388 return "Flow context"; //$NON-NLS-1$
391 public FlowInfo initsOnBreak() {
393 return FlowInfo.DEAD_END;
396 public UnconditionalFlowInfo initsOnReturn() {
398 return FlowInfo.DEAD_END;
401 public boolean isBreakable() {
406 public boolean isContinuable() {
411 public boolean isNonReturningContext() {
416 public boolean isSubRoutine() {
421 public char[] labelName() {
426 public void recordBreakFrom(FlowInfo flowInfo) {
427 // default implementation: do nothing
430 public void recordContinueFrom(FlowInfo flowInfo) {
431 // default implementation: do nothing
434 protected boolean recordFinalAssignment(
435 VariableBinding variable,
436 Reference finalReference) {
438 return true; // keep going
441 protected boolean recordNullReference(Expression expression, int status) {
443 return false; // keep going
446 public void recordReturnFrom(FlowInfo flowInfo) {
447 // default implementation: do nothing
450 public void recordSettingFinal(
451 VariableBinding variable,
452 Reference finalReference,
455 if (!flowInfo.isReachable()) return;
457 // for initialization inside looping statement that effectively loops
458 FlowContext context = this;
459 while (context != null) {
460 if (!context.recordFinalAssignment(variable, finalReference)) {
461 break; // no need to keep going
463 context = context.parent;
467 public void recordUsingNullReference(Scope scope, LocalVariableBinding local, Expression reference, int status, FlowInfo flowInfo) {
469 if (!flowInfo.isReachable()) return;
473 if (flowInfo.isDefinitelyNull(local)) {
474 scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
476 } else if (flowInfo.isDefinitelyNonNull(local)) {
477 scope.problemReporter().localVariableCannotBeNull(local, reference);
481 case FlowInfo.NON_NULL :
482 if (flowInfo.isDefinitelyNull(local)) {
483 scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
489 // for initialization inside looping statement that effectively loops
490 FlowContext context = this;
491 while (context != null) {
492 if (context.recordNullReference(reference, status)) {
493 return; // no need to keep going
495 context = context.parent;
499 void removeFinalAssignmentIfAny(Reference reference) {
500 // default implementation: do nothing
503 public SubRoutineStatement subRoutine() {
508 public String toString() {
510 StringBuffer buffer = new StringBuffer();
511 FlowContext current = this;
512 int parentsCount = 0;
513 while ((current = current.parent) != null) {
516 FlowContext[] parents = new FlowContext[parentsCount + 1];
518 int index = parentsCount;
520 parents[index--] = current;
521 current = current.parent;
523 for (int i = 0; i < parentsCount; i++) {
524 for (int j = 0; j < i; j++)
526 buffer.append(parents[i].individualToString()).append('\n');
529 for (int j = 0; j < parentsCount + 1; j++)
531 buffer.append(individualToString()).append('\n');
532 return buffer.toString();