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.Reference;
17 import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
18 import org.eclipse.jdt.internal.compiler.ast.TryStatement;
19 import org.eclipse.jdt.internal.compiler.codegen.Label;
20 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
21 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
22 import org.eclipse.jdt.internal.compiler.lookup.Scope;
23 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
24 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
25 import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
28 * Reflects the context of code analysis, keeping track of enclosing
29 * try statements, exception handlers, etc...
31 public class FlowContext implements TypeConstants {
33 public ASTNode associatedNode;
34 public FlowContext parent;
36 public final static FlowContext NotContinuableContext = new FlowContext(null, null);
38 public FlowContext(FlowContext parent, ASTNode associatedNode) {
41 this.associatedNode = associatedNode;
44 public Label breakLabel() {
49 public void checkExceptionHandlers(
50 TypeBinding[] raisedExceptions,
55 // check that all the argument exception types are handled
56 // JDK Compatible implementation - when an exception type is thrown,
57 // all related catch blocks are marked as reachable... instead of those only
58 // until the point where it is safely handled (Smarter - see comment at the end)
59 int remainingCount; // counting the number of remaining unhandled exceptions
60 int raisedCount; // total number of exceptions raised
61 if ((raisedExceptions == null)
62 || ((raisedCount = raisedExceptions.length) == 0))
64 remainingCount = raisedCount;
66 // duplicate the array of raised exceptions since it will be updated
67 // (null replaces any handled exception)
71 (raisedExceptions = new TypeBinding[raisedCount]),
74 FlowContext traversedContext = this;
76 while (traversedContext != null) {
77 SubRoutineStatement sub;
78 if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
79 // traversing a non-returning subroutine means that all unhandled
80 // exceptions will actually never get sent...
83 // filter exceptions that are locally caught from the innermost enclosing
84 // try statement to the outermost ones.
85 if (traversedContext instanceof ExceptionHandlingFlowContext) {
86 ExceptionHandlingFlowContext exceptionContext =
87 (ExceptionHandlingFlowContext) traversedContext;
88 ReferenceBinding[] caughtExceptions;
89 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
90 int caughtCount = caughtExceptions.length;
91 boolean[] locallyCaught = new boolean[raisedCount]; // at most
93 for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
94 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
95 for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
96 TypeBinding raisedException;
97 if ((raisedException = raisedExceptions[raisedIndex]) != null) {
98 int state = caughtException == null
99 ? EqualOrMoreSpecific /* any exception */
100 : Scope.compareTypes(raisedException, caughtException);
102 case EqualOrMoreSpecific :
103 exceptionContext.recordHandlingException(
105 flowInfo.unconditionalInits(),
108 locallyCaught[raisedIndex]);
109 // was already definitely caught ?
110 if (!locallyCaught[raisedIndex]) {
111 locallyCaught[raisedIndex] = true;
112 // remember that this exception has been definitely caught
117 exceptionContext.recordHandlingException(
119 flowInfo.unconditionalInits(),
123 // was not caught already per construction
128 // remove locally caught exceptions from the remaining ones
129 for (int i = 0; i < raisedCount; i++) {
130 if (locallyCaught[i]) {
131 raisedExceptions[i] = null; // removed from the remaining ones.
135 // method treatment for unchecked exceptions
136 if (exceptionContext.isMethodContext) {
137 for (int i = 0; i < raisedCount; i++) {
138 TypeBinding raisedException;
139 if ((raisedException = raisedExceptions[i]) != null) {
140 if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
141 || raisedException.isCompatibleWith(scope.getJavaLangError())) {
143 raisedExceptions[i] = null;
147 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
148 // clause will be fixed up later as per JLS 8.6).
149 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
150 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
151 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
153 for (int i = 0; i < raisedCount; i++) {
154 TypeBinding raisedException;
155 if ((raisedException = raisedExceptions[i]) != null) {
156 exceptionContext.mergeUnhandledException(raisedException);
159 return; // no need to complain, will fix up constructor exceptions
162 break; // not handled anywhere, thus jump to error handling
165 if (remainingCount == 0)
168 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
169 if (traversedContext.associatedNode instanceof TryStatement){
170 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
172 traversedContext = traversedContext.parent;
174 // if reaches this point, then there are some remaining unhandled exception types.
175 nextReport: for (int i = 0; i < raisedCount; i++) {
176 TypeBinding exception;
177 if ((exception = raisedExceptions[i]) != null) {
178 // only one complaint if same exception declared to be thrown more than once
179 for (int j = 0; j < i; j++) {
180 if (raisedExceptions[j] == exception) continue nextReport; // already reported
182 scope.problemReporter().unhandledException(exception, location);
187 public void checkExceptionHandlers(
188 TypeBinding raisedException,
193 // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
194 // check that all the argument exception types are handled
195 // JDK Compatible implementation - when an exception type is thrown,
196 // all related catch blocks are marked as reachable... instead of those only
197 // until the point where it is safely handled (Smarter - see comment at the end)
198 FlowContext traversedContext = this;
199 while (traversedContext != null) {
200 SubRoutineStatement sub;
201 if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
202 // traversing a non-returning subroutine means that all unhandled
203 // exceptions will actually never get sent...
207 // filter exceptions that are locally caught from the innermost enclosing
208 // try statement to the outermost ones.
209 if (traversedContext instanceof ExceptionHandlingFlowContext) {
210 ExceptionHandlingFlowContext exceptionContext =
211 (ExceptionHandlingFlowContext) traversedContext;
212 ReferenceBinding[] caughtExceptions;
213 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
214 boolean definitelyCaught = false;
215 for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
216 caughtIndex < caughtCount;
218 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
219 int state = caughtException == null
220 ? EqualOrMoreSpecific /* any exception */
221 : Scope.compareTypes(raisedException, caughtException);
223 case EqualOrMoreSpecific :
224 exceptionContext.recordHandlingException(
226 flowInfo.unconditionalInits(),
230 // was it already definitely caught ?
231 definitelyCaught = true;
234 exceptionContext.recordHandlingException(
236 flowInfo.unconditionalInits(),
240 // was not caught already per construction
243 if (definitelyCaught)
246 // method treatment for unchecked exceptions
247 if (exceptionContext.isMethodContext) {
248 if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
249 || raisedException.isCompatibleWith(scope.getJavaLangError()))
252 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
253 // clause will be fixed up later as per JLS 8.6).
254 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
255 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
256 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
258 exceptionContext.mergeUnhandledException(raisedException);
259 return; // no need to complain, will fix up constructor exceptions
262 break; // not handled anywhere, thus jump to error handling
266 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
267 if (traversedContext.associatedNode instanceof TryStatement){
268 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
270 traversedContext = traversedContext.parent;
272 // if reaches this point, then there are some remaining unhandled exception types.
273 scope.problemReporter().unhandledException(raisedException, location);
276 public Label continueLabel() {
282 * lookup through break labels
284 public FlowContext getTargetContextForBreakLabel(char[] labelName) {
286 FlowContext current = this, lastNonReturningSubRoutine = null;
287 while (current != null) {
288 if (current.isNonReturningContext()) {
289 lastNonReturningSubRoutine = current;
291 char[] currentLabelName;
292 if (((currentLabelName = current.labelName()) != null)
293 && CharOperation.equals(currentLabelName, labelName)) {
294 if (lastNonReturningSubRoutine == null)
296 return lastNonReturningSubRoutine;
298 current = current.parent;
305 * lookup through continue labels
307 public FlowContext getTargetContextForContinueLabel(char[] labelName) {
309 FlowContext current = this;
310 FlowContext lastContinuable = null;
311 FlowContext lastNonReturningSubRoutine = null;
313 while (current != null) {
314 if (current.isNonReturningContext()) {
315 lastNonReturningSubRoutine = current;
317 if (current.isContinuable()) {
318 lastContinuable = current;
322 char[] currentLabelName;
323 if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
325 // matching label found
326 if ((lastContinuable != null)
327 && (current.associatedNode.concreteStatement() == lastContinuable.associatedNode)) {
329 if (lastNonReturningSubRoutine == null) return lastContinuable;
330 return lastNonReturningSubRoutine;
332 // label is found, but not a continuable location
333 return NotContinuableContext;
335 current = current.parent;
342 * lookup a default break through breakable locations
344 public FlowContext getTargetContextForDefaultBreak() {
346 FlowContext current = this, lastNonReturningSubRoutine = null;
347 while (current != null) {
348 if (current.isNonReturningContext()) {
349 lastNonReturningSubRoutine = current;
351 if (current.isBreakable() && current.labelName() == null) {
352 if (lastNonReturningSubRoutine == null) return current;
353 return lastNonReturningSubRoutine;
355 current = current.parent;
362 * lookup a default continue amongst continuable locations
364 public FlowContext getTargetContextForDefaultContinue() {
366 FlowContext current = this, lastNonReturningSubRoutine = null;
367 while (current != null) {
368 if (current.isNonReturningContext()) {
369 lastNonReturningSubRoutine = current;
371 if (current.isContinuable()) {
372 if (lastNonReturningSubRoutine == null)
374 return lastNonReturningSubRoutine;
376 current = current.parent;
382 public String individualToString() {
384 return "Flow context"; //$NON-NLS-1$
387 public FlowInfo initsOnBreak() {
389 return FlowInfo.DEAD_END;
392 public UnconditionalFlowInfo initsOnReturn() {
394 return FlowInfo.DEAD_END;
397 public boolean isBreakable() {
402 public boolean isContinuable() {
407 public boolean isNonReturningContext() {
412 public boolean isSubRoutine() {
417 public char[] labelName() {
422 public void recordBreakFrom(FlowInfo flowInfo) {
423 // default implementation: do nothing
426 public void recordContinueFrom(FlowInfo flowInfo) {
427 // default implementation: do nothing
430 boolean recordFinalAssignment(
431 VariableBinding variable,
432 Reference finalReference) {
434 return true; // keep going
437 public void recordReturnFrom(FlowInfo flowInfo) {
438 // default implementation: do nothing
441 public void recordSettingFinal(
442 VariableBinding variable,
443 Reference finalReference,
446 if (!flowInfo.isReachable()) return;
448 // for initialization inside looping statement that effectively loops
449 FlowContext context = this;
450 while (context != null) {
451 if (!context.recordFinalAssignment(variable, finalReference)) {
452 break; // no need to keep going
454 context = context.parent;
458 void removeFinalAssignmentIfAny(Reference reference) {
459 // default implementation: do nothing
462 public SubRoutineStatement subRoutine() {
467 public String toString() {
469 StringBuffer buffer = new StringBuffer();
470 FlowContext current = this;
471 int parentsCount = 0;
472 while ((current = current.parent) != null) {
475 FlowContext[] parents = new FlowContext[parentsCount + 1];
477 int index = parentsCount;
479 parents[index--] = current;
480 current = current.parent;
482 for (int i = 0; i < parentsCount; i++) {
483 for (int j = 0; j < i; j++)
485 buffer.append(parents[i].individualToString()).append('\n');
488 for (int j = 0; j < parentsCount + 1; j++)
490 buffer.append(individualToString()).append('\n');
491 return buffer.toString();