1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.flow;
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.ast.ASTNode;
15 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
16 import net.sourceforge.phpdt.internal.compiler.ast.Reference;
17 import net.sourceforge.phpdt.internal.compiler.ast.TryStatement;
18 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
19 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
20 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
21 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
22 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
23 import net.sourceforge.phpdt.internal.compiler.lookup.TypeConstants;
24 import net.sourceforge.phpdt.internal.compiler.lookup.VariableBinding;
27 * Reflects the context of code analysis, keeping track of enclosing try
28 * statements, exception handlers, etc...
30 public class FlowContext implements TypeConstants {
32 public ASTNode associatedNode;
34 public FlowContext parent;
36 public final static FlowContext NotContinuableContext = new FlowContext(
39 public FlowContext(FlowContext parent, ASTNode associatedNode) {
42 this.associatedNode = associatedNode;
45 public Label breakLabel() {
50 public void checkExceptionHandlers(TypeBinding[] raisedExceptions,
51 ASTNode location, FlowInfo flowInfo, BlockScope scope) {
53 // check that all the argument exception types are handled
54 // JDK Compatible implementation - when an exception type is thrown,
55 // all related catch blocks are marked as reachable... instead of those
57 // until the point where it is safely handled (Smarter - see comment at
59 int remainingCount; // counting the number of remaining unhandled
61 int raisedCount; // total number of exceptions raised
62 if ((raisedExceptions == null)
63 || ((raisedCount = raisedExceptions.length) == 0))
65 remainingCount = raisedCount;
67 // duplicate the array of raised exceptions since it will be updated
68 // (null replaces any handled exception)
69 System.arraycopy(raisedExceptions, 0,
70 (raisedExceptions = new TypeBinding[raisedCount]), 0,
72 FlowContext traversedContext = this;
74 while (traversedContext != null) {
76 if (((sub = traversedContext.subRoutine()) != null)
77 && sub.cannotReturn()) {
78 // traversing a non-returning subroutine means that all
80 // exceptions will actually never get sent...
83 // filter exceptions that are locally caught from the innermost
85 // try statement to the outermost ones.
86 if (traversedContext instanceof ExceptionHandlingFlowContext) {
87 ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) traversedContext;
88 ReferenceBinding[] caughtExceptions;
89 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
90 int caughtCount = caughtExceptions.length;
91 boolean[] locallyCaught = new boolean[raisedCount]; // at
94 for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
95 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
96 for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
97 TypeBinding raisedException;
98 if ((raisedException = raisedExceptions[raisedIndex]) != null) {
99 switch (Scope.compareTypes(raisedException,
101 case EqualOrMoreSpecific:
102 exceptionContext.recordHandlingException(
103 caughtException, flowInfo
104 .unconditionalInits(),
105 raisedException, location,
106 locallyCaught[raisedIndex]);
107 // was already definitely caught ?
108 if (!locallyCaught[raisedIndex]) {
109 locallyCaught[raisedIndex] = true;
110 // remember that this exception has been
116 exceptionContext.recordHandlingException(
117 caughtException, flowInfo
118 .unconditionalInits(),
119 raisedException, location, false);
120 // was not caught already per construction
125 // remove locally caught exceptions from the remaining ones
126 for (int i = 0; i < raisedCount; i++) {
127 if (locallyCaught[i]) {
128 raisedExceptions[i] = null; // removed from the
133 // method treatment for unchecked exceptions
134 if (exceptionContext.isMethodContext) {
135 for (int i = 0; i < raisedCount; i++) {
136 TypeBinding raisedException;
137 if ((raisedException = raisedExceptions[i]) != null) {
138 if (raisedException.isCompatibleWith(scope
139 .getJavaLangRuntimeException())
140 || raisedException.isCompatibleWith(scope
141 .getJavaLangError())) {
143 raisedExceptions[i] = null;
147 // anonymous constructors are allowed to throw any
148 // exceptions (their thrown exceptions
149 // clause will be fixed up later as per JLS 8.6).
150 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration) {
151 AbstractMethodDeclaration method = (AbstractMethodDeclaration) exceptionContext.associatedNode;
152 if (method.isConstructor()
153 && method.binding.declaringClass
154 .isAnonymousType()) {
156 for (int i = 0; i < raisedCount; i++) {
157 TypeBinding raisedException;
158 if ((raisedException = raisedExceptions[i]) != null) {
160 .mergeUnhandledException(raisedException);
163 return; // no need to complain, will fix up
164 // constructor exceptions
167 break; // not handled anywhere, thus jump to error handling
170 if (remainingCount == 0)
173 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
174 if (traversedContext.associatedNode instanceof TryStatement) {
177 .addInitializationsFrom(
178 ((TryStatement) traversedContext.associatedNode).subRoutineInits);
180 traversedContext = traversedContext.parent;
182 // if reaches this point, then there are some remaining unhandled
184 nextReport: for (int i = 0; i < raisedCount; i++) {
185 TypeBinding exception;
186 if ((exception = raisedExceptions[i]) != null) {
187 // only one complaint if same exception declared to be thrown
189 for (int j = 0; j < i; j++) {
190 if (raisedExceptions[j] == exception)
191 continue nextReport; // already reported
193 scope.problemReporter().unhandledException(exception, location);
198 public void checkExceptionHandlers(TypeBinding raisedException,
199 ASTNode location, FlowInfo flowInfo, BlockScope scope) {
201 // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
202 // check that all the argument exception types are handled
203 // JDK Compatible implementation - when an exception type is thrown,
204 // all related catch blocks are marked as reachable... instead of those
206 // until the point where it is safely handled (Smarter - see comment at
208 FlowContext traversedContext = this;
209 while (traversedContext != null) {
211 if (((sub = traversedContext.subRoutine()) != null)
212 && sub.cannotReturn()) {
213 // traversing a non-returning subroutine means that all
215 // exceptions will actually never get sent...
219 // filter exceptions that are locally caught from the innermost
221 // try statement to the outermost ones.
222 if (traversedContext instanceof ExceptionHandlingFlowContext) {
223 ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) traversedContext;
224 ReferenceBinding[] caughtExceptions;
225 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
226 boolean definitelyCaught = false;
227 for (int caughtIndex = 0, caughtCount = caughtExceptions.length; caughtIndex < caughtCount; caughtIndex++) {
228 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
229 switch (Scope.compareTypes(raisedException,
231 case EqualOrMoreSpecific:
233 .recordHandlingException(caughtException,
234 flowInfo.unconditionalInits(),
235 raisedException, location,
237 // was it already definitely caught ?
238 definitelyCaught = true;
241 exceptionContext.recordHandlingException(
242 caughtException, flowInfo
243 .unconditionalInits(),
244 raisedException, location, false);
245 // was not caught already per construction
248 if (definitelyCaught)
251 // method treatment for unchecked exceptions
252 if (exceptionContext.isMethodContext) {
253 if (raisedException.isCompatibleWith(scope
254 .getJavaLangRuntimeException())
255 || raisedException.isCompatibleWith(scope
256 .getJavaLangError()))
259 // anonymous constructors are allowed to throw any
260 // exceptions (their thrown exceptions
261 // clause will be fixed up later as per JLS 8.6).
262 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration) {
263 AbstractMethodDeclaration method = (AbstractMethodDeclaration) exceptionContext.associatedNode;
264 if (method.isConstructor()
265 && method.binding.declaringClass
266 .isAnonymousType()) {
269 .mergeUnhandledException(raisedException);
270 return; // no need to complain, will fix up
271 // constructor exceptions
274 break; // not handled anywhere, thus jump to error handling
278 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
279 if (traversedContext.associatedNode instanceof TryStatement) {
282 .addInitializationsFrom(
283 ((TryStatement) traversedContext.associatedNode).subRoutineInits);
285 traversedContext = traversedContext.parent;
287 // if reaches this point, then there are some remaining unhandled
289 scope.problemReporter().unhandledException(raisedException, location);
292 public Label continueLabel() {
298 * lookup through break labels
300 public FlowContext getTargetContextForBreakLabel(char[] labelName) {
302 FlowContext current = this, lastNonReturningSubRoutine = null;
303 while (current != null) {
304 if (current.isNonReturningContext()) {
305 lastNonReturningSubRoutine = current;
307 char[] currentLabelName;
308 if (((currentLabelName = current.labelName()) != null)
309 && CharOperation.equals(currentLabelName, labelName)) {
310 if (lastNonReturningSubRoutine == null) {
313 return lastNonReturningSubRoutine;
316 current = current.parent;
323 * lookup through continue labels
325 public FlowContext getTargetContextForContinueLabel(char[] labelName) {
327 FlowContext current = this;
328 FlowContext lastContinuable = null;
329 FlowContext lastNonReturningSubRoutine = null;
331 while (current != null) {
332 if (current.isNonReturningContext()) {
333 lastNonReturningSubRoutine = current;
335 if (current.isContinuable()) {
336 lastContinuable = current;
340 char[] currentLabelName;
341 if ((currentLabelName = current.labelName()) != null
342 && CharOperation.equals(currentLabelName, labelName)) {
344 // matching label found
345 if ((lastContinuable != null)
346 && (current.associatedNode.concreteStatement() == lastContinuable.associatedNode)) {
348 if (lastNonReturningSubRoutine == null) {
349 return lastContinuable;
351 return lastNonReturningSubRoutine;
354 // label is found, but not a continuable location
355 return NotContinuableContext;
358 current = current.parent;
365 * lookup a default break through breakable locations
367 public FlowContext getTargetContextForDefaultBreak() {
369 FlowContext current = this, lastNonReturningSubRoutine = null;
370 while (current != null) {
371 if (current.isNonReturningContext()) {
372 lastNonReturningSubRoutine = current;
374 if (current.isBreakable() && current.labelName() == null) {
375 if (lastNonReturningSubRoutine == null) {
378 return lastNonReturningSubRoutine;
381 current = current.parent;
388 * lookup a default continue amongst continuable locations
390 public FlowContext getTargetContextForDefaultContinue() {
392 FlowContext current = this, lastNonReturningSubRoutine = null;
393 while (current != null) {
394 if (current.isNonReturningContext()) {
395 lastNonReturningSubRoutine = current;
397 if (current.isContinuable()) {
398 if (lastNonReturningSubRoutine == null) {
401 return lastNonReturningSubRoutine;
404 current = current.parent;
410 public String individualToString() {
412 return "Flow context"; //$NON-NLS-1$
415 public FlowInfo initsOnBreak() {
417 return FlowInfo.DEAD_END;
420 public UnconditionalFlowInfo initsOnReturn() {
422 return FlowInfo.DEAD_END;
425 public boolean isBreakable() {
430 public boolean isContinuable() {
435 public boolean isNonReturningContext() {
440 public boolean isSubRoutine() {
445 public char[] labelName() {
450 public void recordBreakFrom(FlowInfo flowInfo) {
453 public void recordContinueFrom(FlowInfo flowInfo) {
456 boolean recordFinalAssignment(VariableBinding variable,
457 Reference finalReference) {
459 return true; // keep going
462 public void recordReturnFrom(FlowInfo flowInfo) {
465 public void recordSettingFinal(VariableBinding variable,
466 Reference finalReference) {
468 // for initialization inside looping statement that effectively loops
469 FlowContext context = this;
470 while (context != null) {
471 if (!context.recordFinalAssignment(variable, finalReference)) {
472 break; // no need to keep going
474 context = context.parent;
478 void removeFinalAssignmentIfAny(Reference reference) {
481 public ASTNode subRoutine() {
486 public String toString() {
488 StringBuffer buffer = new StringBuffer();
489 FlowContext current = this;
490 int parentsCount = 0;
491 while ((current = current.parent) != null) {
494 FlowContext[] parents = new FlowContext[parentsCount + 1];
496 int index = parentsCount;
498 parents[index--] = current;
499 current = current.parent;
501 for (int i = 0; i < parentsCount; i++) {
502 for (int j = 0; j < i; j++)
504 buffer.append(parents[i].individualToString()).append('\n');
507 for (int j = 0; j < parentsCount + 1; j++)
509 buffer.append(individualToString()).append('\n');
510 return buffer.toString();