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.codegen.Label;
15 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
16 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
17 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
18 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.TypeConstants;
20 import net.sourceforge.phpdt.internal.compiler.lookup.VariableBinding;
21 import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
22 import net.sourceforge.phpeclipse.internal.compiler.ast.AstNode;
23 import net.sourceforge.phpeclipse.internal.compiler.ast.Reference;
24 import net.sourceforge.phpeclipse.internal.compiler.ast.TryStatement;
27 * Reflects the context of code analysis, keeping track of enclosing
28 * try statements, exception handlers, etc...
30 public class FlowContext implements TypeConstants {
32 public AstNode associatedNode;
33 public FlowContext parent;
35 public final static FlowContext NotContinuableContext = new FlowContext(null, null);
37 public FlowContext(FlowContext parent, AstNode associatedNode) {
40 this.associatedNode = associatedNode;
43 public Label breakLabel() {
48 public void checkExceptionHandlers(
49 TypeBinding[] raisedExceptions,
54 // check that all the argument exception types are handled
55 // JDK Compatible implementation - when an exception type is thrown,
56 // all related catch blocks are marked as reachable... instead of those only
57 // until the point where it is safely handled (Smarter - see comment at the end)
58 int remainingCount; // counting the number of remaining unhandled exceptions
59 int raisedCount; // total number of exceptions raised
60 if ((raisedExceptions == null)
61 || ((raisedCount = raisedExceptions.length) == 0))
63 remainingCount = raisedCount;
65 // duplicate the array of raised exceptions since it will be updated
66 // (null replaces any handled exception)
70 (raisedExceptions = new TypeBinding[raisedCount]),
73 FlowContext traversedContext = this;
75 while (traversedContext != null) {
77 if (((sub = traversedContext.subRoutine()) != null) && sub.cannotReturn()) {
78 // traversing a non-returning subroutine means that all unhandled
79 // exceptions will actually never get sent...
82 // filter exceptions that are locally caught from the innermost enclosing
83 // try statement to the outermost ones.
84 if (traversedContext instanceof ExceptionHandlingFlowContext) {
85 ExceptionHandlingFlowContext exceptionContext =
86 (ExceptionHandlingFlowContext) traversedContext;
87 ReferenceBinding[] caughtExceptions;
88 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
89 int caughtCount = caughtExceptions.length;
90 boolean[] locallyCaught = new boolean[raisedCount]; // at most
92 for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
93 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
94 for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
95 TypeBinding raisedException;
96 if ((raisedException = raisedExceptions[raisedIndex]) != null) {
97 switch (Scope.compareTypes(raisedException, caughtException)) {
98 case EqualOrMoreSpecific :
99 exceptionContext.recordHandlingException(
101 flowInfo.unconditionalInits(),
104 locallyCaught[raisedIndex]);
105 // was already definitely caught ?
106 if (!locallyCaught[raisedIndex]) {
107 locallyCaught[raisedIndex] = true;
108 // remember that this exception has been definitely caught
113 exceptionContext.recordHandlingException(
115 flowInfo.unconditionalInits(),
119 // was not caught already per construction
124 // remove locally caught exceptions from the remaining ones
125 for (int i = 0; i < raisedCount; i++) {
126 if (locallyCaught[i]) {
127 raisedExceptions[i] = null; // removed from the remaining ones.
131 // method treatment for unchecked exceptions
132 if (exceptionContext.isMethodContext) {
133 for (int i = 0; i < raisedCount; i++) {
134 TypeBinding raisedException;
135 if ((raisedException = raisedExceptions[i]) != null) {
136 if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
137 || raisedException.isCompatibleWith(scope.getJavaLangError())) {
139 raisedExceptions[i] = null;
143 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
144 // clause will be fixed up later as per JLS 8.6).
145 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
146 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
147 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
149 for (int i = 0; i < raisedCount; i++) {
150 TypeBinding raisedException;
151 if ((raisedException = raisedExceptions[i]) != null) {
152 exceptionContext.mergeUnhandledException(raisedException);
155 return; // no need to complain, will fix up constructor exceptions
158 break; // not handled anywhere, thus jump to error handling
161 if (remainingCount == 0)
164 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
165 if (traversedContext.associatedNode instanceof TryStatement){
166 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
168 traversedContext = traversedContext.parent;
170 // if reaches this point, then there are some remaining unhandled exception types.
171 nextReport: for (int i = 0; i < raisedCount; i++) {
172 TypeBinding exception;
173 if ((exception = raisedExceptions[i]) != null) {
174 // only one complaint if same exception declared to be thrown more than once
175 for (int j = 0; j < i; j++) {
176 if (raisedExceptions[j] == exception) continue nextReport; // already reported
178 scope.problemReporter().unhandledException(exception, location);
183 public void checkExceptionHandlers(
184 TypeBinding raisedException,
189 // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
190 // check that all the argument exception types are handled
191 // JDK Compatible implementation - when an exception type is thrown,
192 // all related catch blocks are marked as reachable... instead of those only
193 // until the point where it is safely handled (Smarter - see comment at the end)
194 FlowContext traversedContext = this;
195 while (traversedContext != null) {
197 if (((sub = traversedContext.subRoutine()) != null) && sub.cannotReturn()) {
198 // traversing a non-returning subroutine means that all unhandled
199 // exceptions will actually never get sent...
203 // filter exceptions that are locally caught from the innermost enclosing
204 // try statement to the outermost ones.
205 if (traversedContext instanceof ExceptionHandlingFlowContext) {
206 ExceptionHandlingFlowContext exceptionContext =
207 (ExceptionHandlingFlowContext) traversedContext;
208 ReferenceBinding[] caughtExceptions;
209 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
210 boolean definitelyCaught = false;
211 for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
212 caughtIndex < caughtCount;
214 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
215 switch (Scope.compareTypes(raisedException, caughtException)) {
216 case EqualOrMoreSpecific :
217 exceptionContext.recordHandlingException(
219 flowInfo.unconditionalInits(),
223 // was it already definitely caught ?
224 definitelyCaught = true;
227 exceptionContext.recordHandlingException(
229 flowInfo.unconditionalInits(),
233 // was not caught already per construction
236 if (definitelyCaught)
239 // method treatment for unchecked exceptions
240 if (exceptionContext.isMethodContext) {
241 if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
242 || raisedException.isCompatibleWith(scope.getJavaLangError()))
245 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
246 // clause will be fixed up later as per JLS 8.6).
247 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
248 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
249 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
251 exceptionContext.mergeUnhandledException(raisedException);
252 return; // no need to complain, will fix up constructor exceptions
255 break; // not handled anywhere, thus jump to error handling
259 traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
260 if (traversedContext.associatedNode instanceof TryStatement){
261 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
263 traversedContext = traversedContext.parent;
265 // if reaches this point, then there are some remaining unhandled exception types.
266 scope.problemReporter().unhandledException(raisedException, location);
269 public Label continueLabel() {
275 * lookup through break labels
277 public FlowContext getTargetContextForBreakLabel(char[] labelName) {
279 FlowContext current = this, lastNonReturningSubRoutine = null;
280 while (current != null) {
281 if (current.isNonReturningContext()) {
282 lastNonReturningSubRoutine = current;
284 char[] currentLabelName;
285 if (((currentLabelName = current.labelName()) != null)
286 && CharOperation.equals(currentLabelName, labelName)) {
287 if (lastNonReturningSubRoutine == null) {
290 return lastNonReturningSubRoutine;
293 current = current.parent;
300 * lookup through continue labels
302 public FlowContext getTargetContextForContinueLabel(char[] labelName) {
304 FlowContext current = this;
305 FlowContext lastContinuable = null;
306 FlowContext lastNonReturningSubRoutine = null;
308 while (current != null) {
309 if (current.isNonReturningContext()) {
310 lastNonReturningSubRoutine = current;
312 if (current.isContinuable()) {
313 lastContinuable = current;
317 char[] currentLabelName;
318 if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
320 // matching label found
321 if ((lastContinuable != null)
322 && (current.associatedNode.concreteStatement() == lastContinuable.associatedNode)) {
324 if (lastNonReturningSubRoutine == null) {
325 return lastContinuable;
327 return lastNonReturningSubRoutine;
330 // label is found, but not a continuable location
331 return NotContinuableContext;
334 current = current.parent;
341 * lookup a default break through breakable locations
343 public FlowContext getTargetContextForDefaultBreak() {
345 FlowContext current = this, lastNonReturningSubRoutine = null;
346 while (current != null) {
347 if (current.isNonReturningContext()) {
348 lastNonReturningSubRoutine = current;
350 if (current.isBreakable() && current.labelName() == null) {
351 if (lastNonReturningSubRoutine == null) {
354 return lastNonReturningSubRoutine;
357 current = current.parent;
364 * lookup a default continue amongst continuable locations
366 public FlowContext getTargetContextForDefaultContinue() {
368 FlowContext current = this, lastNonReturningSubRoutine = null;
369 while (current != null) {
370 if (current.isNonReturningContext()) {
371 lastNonReturningSubRoutine = current;
373 if (current.isContinuable()) {
374 if (lastNonReturningSubRoutine == null) {
377 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) {
429 public void recordContinueFrom(FlowInfo flowInfo) {
432 boolean recordFinalAssignment(
433 VariableBinding variable,
434 Reference finalReference) {
436 return true; // keep going
439 public void recordReturnFrom(FlowInfo flowInfo) {
442 public void recordSettingFinal(
443 VariableBinding variable,
444 Reference finalReference) {
446 // for initialization inside looping statement that effectively loops
447 FlowContext context = this;
448 while (context != null) {
449 if (!context.recordFinalAssignment(variable, finalReference)) {
450 break; // no need to keep going
452 context = context.parent;
456 void removeFinalAssignmentIfAny(Reference reference) {
459 public AstNode subRoutine() {
464 public String toString() {
466 StringBuffer buffer = new StringBuffer();
467 FlowContext current = this;
468 int parentsCount = 0;
469 while ((current = current.parent) != null) {
472 FlowContext[] parents = new FlowContext[parentsCount + 1];
474 int index = parentsCount;
476 parents[index--] = current;
477 current = current.parent;
479 for (int i = 0; i < parentsCount; i++) {
480 for (int j = 0; j < i; j++)
482 buffer.append(parents[i].individualToString()).append('\n');
485 for (int j = 0; j < parentsCount + 1; j++)
487 buffer.append(individualToString()).append('\n');
488 return buffer.toString();