1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.flow;
13 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
14 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
15 import net.sourceforge.phpdt.internal.compiler.ast.Reference;
16 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
17 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
18 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
20 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
21 import net.sourceforge.phpdt.internal.compiler.lookup.TypeConstants;
22 import net.sourceforge.phpdt.internal.compiler.lookup.VariableBinding;
23 import net.sourceforge.phpdt.internal.compiler.util.CharOperation;
26 * Reflects the context of code analysis, keeping track of enclosing
27 * try statements, exception handlers, etc...
29 public class FlowContext implements TypeConstants {
30 public AstNode associatedNode;
31 public FlowContext parent;
33 public final static FlowContext NotContinuableContext =
34 new FlowContext(null, null);
36 public FlowContext(FlowContext parent, AstNode associatedNode) {
38 this.associatedNode = associatedNode;
41 public Label breakLabel() {
45 public void checkExceptionHandlers(
46 TypeBinding[] raisedExceptions,
51 // check that all the argument exception types are handled
52 // JDK Compatible implementation - when an exception type is thrown,
53 // all related catch blocks are marked as reachable... instead of those only
54 // until the point where it is safely handled (Smarter - see comment at the end)
55 int remainingCount; // counting the number of remaining unhandled exceptions
56 int raisedCount; // total number of exceptions raised
57 if ((raisedExceptions == null)
58 || ((raisedCount = raisedExceptions.length) == 0))
60 remainingCount = raisedCount;
62 // duplicate the array of raised exceptions since it will be updated
63 // (null replaces any handled exception)
67 (raisedExceptions = new TypeBinding[raisedCount]),
70 FlowContext traversedContext = this;
71 while (traversedContext != null) {
73 if (((sub = traversedContext.subRoutine()) != null) && sub.cannotReturn()) {
74 // traversing a non-returning subroutine means that all unhandled
75 // exceptions will actually never get sent...
78 // filter exceptions that are locally caught from the most enclosing
79 // try statement to the outer ones.
80 if (traversedContext instanceof ExceptionHandlingFlowContext) {
81 ExceptionHandlingFlowContext exceptionContext =
82 (ExceptionHandlingFlowContext) traversedContext;
83 ReferenceBinding[] caughtExceptions;
84 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
85 int caughtCount = caughtExceptions.length;
86 boolean[] locallyCaught = new boolean[raisedCount]; // at most
88 for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
89 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
90 for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
91 TypeBinding raisedException;
92 if ((raisedException = raisedExceptions[raisedIndex]) != null) {
93 switch (Scope.compareTypes(raisedException, caughtException)) {
94 case EqualOrMoreSpecific :
95 exceptionContext.recordHandlingException(
97 flowInfo.unconditionalInits(),
100 locallyCaught[raisedIndex]);
101 // was already definitely caught ?
102 if (!locallyCaught[raisedIndex]) {
103 locallyCaught[raisedIndex] = true;
104 // remember that this exception has been definitely caught
109 exceptionContext.recordHandlingException(
111 flowInfo.unconditionalInits(),
115 // was not caught already per construction
120 // remove locally caught exceptions from the remaining ones
121 for (int i = 0; i < raisedCount; i++) {
122 if (locallyCaught[i]) {
123 raisedExceptions[i] = null; // removed from the remaining ones.
127 // method treatment for unchecked exceptions
128 if (exceptionContext.isMethodContext) {
129 for (int i = 0; i < raisedCount; i++) {
130 TypeBinding raisedException;
131 if ((raisedException = raisedExceptions[i]) != null) {
133 .areTypesCompatible(raisedException, scope.getJavaLangRuntimeException())
134 || scope.areTypesCompatible(raisedException, scope.getJavaLangError())) {
136 raisedExceptions[i] = null;
140 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
141 // clause will be fixed up later as per JLS 8.6).
142 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
143 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
144 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
146 for (int i = 0; i < raisedCount; i++) {
147 TypeBinding raisedException;
148 if ((raisedException = raisedExceptions[i]) != null) {
149 exceptionContext.mergeUnhandledException(raisedException);
152 return; // no need to complain, will fix up constructor exceptions
155 break; // not handled anywhere, thus jump to error handling
158 if (remainingCount == 0)
160 traversedContext = traversedContext.parent;
162 // if reaches this point, then there are some remaining unhandled exception types.
163 for (int i = 0; i < raisedCount; i++) {
164 TypeBinding exception;
165 if ((exception = raisedExceptions[i]) != null) {
166 scope.problemReporter().unhandledException(exception, location);
171 public void checkExceptionHandlers(
172 TypeBinding raisedException,
177 // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
178 // check that all the argument exception types are handled
179 // JDK Compatible implementation - when an exception type is thrown,
180 // all related catch blocks are marked as reachable... instead of those only
181 // until the point where it is safely handled (Smarter - see comment at the end)
182 FlowContext traversedContext = this;
183 while (traversedContext != null) {
185 if (((sub = traversedContext.subRoutine()) != null) && sub.cannotReturn()) {
186 // traversing a non-returning subroutine means that all unhandled
187 // exceptions will actually never get sent...
190 // filter exceptions that are locally caught from the most enclosing
191 // try statement to the outer ones.
192 if (traversedContext instanceof ExceptionHandlingFlowContext) {
193 ExceptionHandlingFlowContext exceptionContext =
194 (ExceptionHandlingFlowContext) traversedContext;
195 ReferenceBinding[] caughtExceptions;
196 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
197 boolean definitelyCaught = false;
198 for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
199 caughtIndex < caughtCount;
201 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
202 switch (Scope.compareTypes(raisedException, caughtException)) {
203 case EqualOrMoreSpecific :
204 exceptionContext.recordHandlingException(
206 flowInfo.unconditionalInits(),
210 // was it already definitely caught ?
211 definitelyCaught = true;
214 exceptionContext.recordHandlingException(
216 flowInfo.unconditionalInits(),
220 // was not caught already per construction
223 if (definitelyCaught)
226 // method treatment for unchecked exceptions
227 if (exceptionContext.isMethodContext) {
229 .areTypesCompatible(raisedException, scope.getJavaLangRuntimeException())
230 || scope.areTypesCompatible(raisedException, scope.getJavaLangError()))
233 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
234 // clause will be fixed up later as per JLS 8.6).
235 if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
236 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
237 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
239 exceptionContext.mergeUnhandledException(raisedException);
240 return; // no need to complain, will fix up constructor exceptions
243 break; // not handled anywhere, thus jump to error handling
246 traversedContext = traversedContext.parent;
248 // if reaches this point, then there are some remaining unhandled exception types.
249 scope.problemReporter().unhandledException(raisedException, location);
252 public Label continueLabel() {
257 * lookup through break labels
259 public FlowContext getTargetContextForBreakLabel(char[] labelName) {
260 FlowContext current = this, lastNonReturningSubRoutine = null;
261 while (current != null) {
262 if (current.isNonReturningContext()) {
263 lastNonReturningSubRoutine = current;
265 char[] currentLabelName;
266 if (((currentLabelName = current.labelName()) != null)
267 && CharOperation.equals(currentLabelName, labelName)) {
268 if (lastNonReturningSubRoutine == null) {
271 return lastNonReturningSubRoutine;
274 current = current.parent;
281 * lookup through continue labels
283 public FlowContext getTargetContextForContinueLabel(char[] labelName) {
284 FlowContext current = this,
285 lastContinuable = null,
286 lastNonReturningSubRoutine = null;
287 while (current != null) {
288 if (current.isNonReturningContext()) {
289 lastNonReturningSubRoutine = current;
291 if (current.isContinuable()) {
292 lastContinuable = current;
295 char[] currentLabelName;
296 if (((currentLabelName = current.labelName()) != null)
297 && CharOperation.equals(currentLabelName, labelName)) {
298 if ((lastContinuable != null)
299 && (current.associatedNode.concreteStatement()
300 == lastContinuable.associatedNode)) {
301 if (lastNonReturningSubRoutine == null) {
302 return lastContinuable;
304 return lastNonReturningSubRoutine;
307 // label is found, but not a continuable location
308 return NotContinuableContext;
311 current = current.parent;
318 * lookup a default break through breakable locations
320 public FlowContext getTargetContextForDefaultBreak() {
321 FlowContext current = this, lastNonReturningSubRoutine = null;
322 while (current != null) {
323 if (current.isNonReturningContext()) {
324 lastNonReturningSubRoutine = current;
326 if (current.isBreakable()) {
327 if (lastNonReturningSubRoutine == null) {
330 return lastNonReturningSubRoutine;
333 current = current.parent;
340 * lookup a default continue amongst continuable locations
342 public FlowContext getTargetContextForDefaultContinue() {
343 FlowContext current = this, lastNonReturningSubRoutine = null;
344 while (current != null) {
345 if (current.isNonReturningContext()) {
346 lastNonReturningSubRoutine = current;
348 if (current.isContinuable()) {
349 if (lastNonReturningSubRoutine == null) {
352 return lastNonReturningSubRoutine;
355 current = current.parent;
361 public String individualToString() {
362 return "Flow context"; //$NON-NLS-1$
365 public FlowInfo initsOnBreak() {
366 return FlowInfo.DeadEnd;
369 public boolean isBreakable() {
373 public boolean isContinuable() {
377 public boolean isNonReturningContext() {
381 public boolean isSubRoutine() {
385 public char[] labelName() {
389 public void recordBreakFrom(FlowInfo flowInfo) {
392 public void recordContinueFrom(FlowInfo flowInfo) {
395 boolean recordFinalAssignment(
396 VariableBinding variable,
397 Reference finalReference) {
398 return true; // keep going
401 public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
404 public void recordSettingFinal(
405 VariableBinding variable,
406 Reference finalReference) {
407 // for initialization inside looping statement that effectively loops
408 FlowContext context = this;
409 while (context != null) {
410 if (!context.recordFinalAssignment(variable, finalReference)) {
411 break; // no need to keep going
413 context = context.parent;
417 void removeFinalAssignmentIfAny(Reference reference) {
420 public AstNode subRoutine() {
424 public String toString() {
425 StringBuffer buffer = new StringBuffer();
426 FlowContext current = this;
427 int parentsCount = 0;
428 while ((current = current.parent) != null) {
431 FlowContext[] parents = new FlowContext[parentsCount + 1];
433 int index = parentsCount;
435 parents[index--] = current;
436 current = current.parent;
438 for (int i = 0; i < parentsCount; i++) {
439 for (int j = 0; j < i; j++)
441 buffer.append(parents[i].individualToString()).append('\n');
444 for (int j = 0; j < parentsCount + 1; j++)
446 buffer.append(individualToString()).append('\n');
447 return buffer.toString();