KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > flow > FlowContext


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.flow;
12
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.LabeledStatement;
18 import org.eclipse.jdt.internal.compiler.ast.Reference;
19 import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
20 import org.eclipse.jdt.internal.compiler.ast.TryStatement;
21 import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
22 import org.eclipse.jdt.internal.compiler.lookup.Binding;
23 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
24 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
25 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
26 import org.eclipse.jdt.internal.compiler.lookup.Scope;
27 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
28 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
29 import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
30
31 /**
32  * Reflects the context of code analysis, keeping track of enclosing
33  * try statements, exception handlers, etc...
34  */

35 public class FlowContext implements TypeConstants {
36     
37     // preempt marks looping contexts
38
public final static FlowContext NotContinuableContext = new FlowContext(null, null);
39     public ASTNode associatedNode;
40         public FlowContext parent;
41     public NullInfoRegistry initsOnFinally;
42         // only used within try blocks; remembers upstream flow info mergedWith
43
// any null related operation happening within the try block
44

45 boolean deferNullDiagnostic, preemptNullDiagnostic;
46
47 public FlowContext(FlowContext parent, ASTNode associatedNode) {
48     this.parent = parent;
49     this.associatedNode = associatedNode;
50     if (parent != null) {
51         this.deferNullDiagnostic =
52             parent.deferNullDiagnostic || parent.preemptNullDiagnostic;
53         this.initsOnFinally = parent.initsOnFinally;
54     }
55 }
56
57 public BranchLabel breakLabel() {
58     return null;
59 }
60
61 public void checkExceptionHandlers(TypeBinding raisedException, ASTNode location, FlowInfo flowInfo, BlockScope scope) {
62     // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
63
// check that all the argument exception types are handled
64
// JDK Compatible implementation - when an exception type is thrown,
65
// all related catch blocks are marked as reachable... instead of those only
66
// until the point where it is safely handled (Smarter - see comment at the end)
67
FlowContext traversedContext = this;
68     while (traversedContext != null) {
69         SubRoutineStatement sub;
70         if (((sub = traversedContext.subroutine()) != null) && sub.isSubRoutineEscaping()) {
71             // traversing a non-returning subroutine means that all unhandled
72
// exceptions will actually never get sent...
73
return;
74         }
75         
76         // filter exceptions that are locally caught from the innermost enclosing
77
// try statement to the outermost ones.
78
if (traversedContext instanceof ExceptionHandlingFlowContext) {
79             ExceptionHandlingFlowContext exceptionContext =
80                 (ExceptionHandlingFlowContext) traversedContext;
81             ReferenceBinding[] caughtExceptions;
82             if ((caughtExceptions = exceptionContext.handledExceptions) != Binding.NO_EXCEPTIONS) {
83                 boolean definitelyCaught = false;
84                 for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
85                     caughtIndex < caughtCount;
86                     caughtIndex++) {
87                     ReferenceBinding caughtException = caughtExceptions[caughtIndex];
88                     int state = caughtException == null
89                         ? Scope.EQUAL_OR_MORE_SPECIFIC /* any exception */
90                         : Scope.compareTypes(raisedException, caughtException);
91                     switch (state) {
92                         case Scope.EQUAL_OR_MORE_SPECIFIC :
93                             exceptionContext.recordHandlingException(
94                                 caughtException,
95                                 flowInfo.unconditionalInits(),
96                                 raisedException,
97                                 location,
98                                 definitelyCaught);
99                             // was it already definitely caught ?
100
definitelyCaught = true;
101                             break;
102                         case Scope.MORE_GENERIC :
103                             exceptionContext.recordHandlingException(
104                                 caughtException,
105                                 flowInfo.unconditionalInits(),
106                                 raisedException,
107                                 location,
108                                 false);
109                             // was not caught already per construction
110
}
111                 }
112                 if (definitelyCaught)
113                     return;
114             }
115             // method treatment for unchecked exceptions
116
if (exceptionContext.isMethodContext) {
117                 if (raisedException.isUncheckedException(false))
118                     return;
119                     
120                 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
121
// clause will be fixed up later as per JLS 8.6).
122
if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
123                     AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
124                     if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
125                                 
126                         exceptionContext.mergeUnhandledException(raisedException);
127                         return; // no need to complain, will fix up constructor exceptions
128
}
129                 }
130                 break; // not handled anywhere, thus jump to error handling
131
}
132         }
133
134         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
135         
136         if (traversedContext instanceof InsideSubRoutineFlowContext) {
137             ASTNode node = traversedContext.associatedNode;
138             if (node instanceof TryStatement) {
139                 TryStatement tryStatement = (TryStatement) node;
140                 flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits
141
}
142         }
143         traversedContext = traversedContext.parent;
144     }
145     // if reaches this point, then there are some remaining unhandled exception types.
146
scope.problemReporter().unhandledException(raisedException, location);
147 }
148
149 public void checkExceptionHandlers(TypeBinding[] raisedExceptions, ASTNode location, FlowInfo flowInfo, BlockScope scope) {
150     // check that all the argument exception types are handled
151
// JDK Compatible implementation - when an exception type is thrown,
152
// all related catch blocks are marked as reachable... instead of those only
153
// until the point where it is safely handled (Smarter - see comment at the end)
154
int remainingCount; // counting the number of remaining unhandled exceptions
155
int raisedCount; // total number of exceptions raised
156
if ((raisedExceptions == null)
157         || ((raisedCount = raisedExceptions.length) == 0))
158         return;
159     remainingCount = raisedCount;
160
161     // duplicate the array of raised exceptions since it will be updated
162
// (null replaces any handled exception)
163
System.arraycopy(
164         raisedExceptions,
165         0,
166         (raisedExceptions = new TypeBinding[raisedCount]),
167         0,
168         raisedCount);
169     FlowContext traversedContext = this;
170
171     while (traversedContext != null) {
172         SubRoutineStatement sub;
173         if (((sub = traversedContext.subroutine()) != null) && sub.isSubRoutineEscaping()) {
174             // traversing a non-returning subroutine means that all unhandled
175
// exceptions will actually never get sent...
176
return;
177         }
178         // filter exceptions that are locally caught from the innermost enclosing
179
// try statement to the outermost ones.
180
if (traversedContext instanceof ExceptionHandlingFlowContext) {
181             ExceptionHandlingFlowContext exceptionContext =
182                 (ExceptionHandlingFlowContext) traversedContext;
183             ReferenceBinding[] caughtExceptions;
184             if ((caughtExceptions = exceptionContext.handledExceptions) != Binding.NO_EXCEPTIONS) {
185                 int caughtCount = caughtExceptions.length;
186                 boolean[] locallyCaught = new boolean[raisedCount]; // at most
187

188                 for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
189                     ReferenceBinding caughtException = caughtExceptions[caughtIndex];
190                     for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
191                         TypeBinding raisedException;
192                         if ((raisedException = raisedExceptions[raisedIndex]) != null) {
193                             int state = caughtException == null
194                                 ? Scope.EQUAL_OR_MORE_SPECIFIC /* any exception */
195                                 : Scope.compareTypes(raisedException, caughtException);
196                             switch (state) {
197                                 case Scope.EQUAL_OR_MORE_SPECIFIC :
198                                     exceptionContext.recordHandlingException(
199                                         caughtException,
200                                         flowInfo.unconditionalInits(),
201                                         raisedException,
202                                         location,
203                                         locallyCaught[raisedIndex]);
204                                     // was already definitely caught ?
205
if (!locallyCaught[raisedIndex]) {
206                                         locallyCaught[raisedIndex] = true;
207                                         // remember that this exception has been definitely caught
208
remainingCount--;
209                                     }
210                                     break;
211                                 case Scope.MORE_GENERIC :
212                                     exceptionContext.recordHandlingException(
213                                         caughtException,
214                                         flowInfo.unconditionalInits(),
215                                         raisedException,
216                                         location,
217                                         false);
218                                     // was not caught already per construction
219
}
220                         }
221                     }
222                 }
223                 // remove locally caught exceptions from the remaining ones
224
for (int i = 0; i < raisedCount; i++) {
225                     if (locallyCaught[i]) {
226                         raisedExceptions[i] = null; // removed from the remaining ones.
227
}
228                 }
229             }
230             // method treatment for unchecked exceptions
231
if (exceptionContext.isMethodContext) {
232                 for (int i = 0; i < raisedCount; i++) {
233                     TypeBinding raisedException;
234                     if ((raisedException = raisedExceptions[i]) != null) {
235                         if (raisedException.isUncheckedException(false)) {
236                             remainingCount--;
237                             raisedExceptions[i] = null;
238                         }
239                     }
240                 }
241                 // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
242
// clause will be fixed up later as per JLS 8.6).
243
if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
244                     AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
245                     if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
246                             
247                         for (int i = 0; i < raisedCount; i++) {
248                             TypeBinding raisedException;
249                             if ((raisedException = raisedExceptions[i]) != null) {
250                                 exceptionContext.mergeUnhandledException(raisedException);
251                             }
252                         }
253                         return; // no need to complain, will fix up constructor exceptions
254
}
255                 }
256                 break; // not handled anywhere, thus jump to error handling
257
}
258         }
259         if (remainingCount == 0)
260             return;
261             
262         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
263         
264         if (traversedContext instanceof InsideSubRoutineFlowContext) {
265             ASTNode node = traversedContext.associatedNode;
266             if (node instanceof TryStatement) {
267                 TryStatement tryStatement = (TryStatement) node;
268                 flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits
269
}
270         }
271         traversedContext = traversedContext.parent;
272     }
273     // if reaches this point, then there are some remaining unhandled exception types.
274
nextReport: for (int i = 0; i < raisedCount; i++) {
275         TypeBinding exception;
276         if ((exception = raisedExceptions[i]) != null) {
277             // only one complaint if same exception declared to be thrown more than once
278
for (int j = 0; j < i; j++) {
279                 if (raisedExceptions[j] == exception) continue nextReport; // already reported
280
}
281             scope.problemReporter().unhandledException(exception, location);
282         }
283     }
284 }
285
286 public BranchLabel continueLabel() {
287     return null;
288 }
289
290 /*
291  * lookup through break labels
292  */

293 public FlowContext getTargetContextForBreakLabel(char[] labelName) {
294     FlowContext current = this, lastNonReturningSubRoutine = null;
295     while (current != null) {
296         if (current.isNonReturningContext()) {
297             lastNonReturningSubRoutine = current;
298         }
299         char[] currentLabelName;
300         if (((currentLabelName = current.labelName()) != null)
301             && CharOperation.equals(currentLabelName, labelName)) {
302             ((LabeledStatement)current.associatedNode).bits |= ASTNode.LabelUsed;
303             if (lastNonReturningSubRoutine == null)
304                 return current;
305             return lastNonReturningSubRoutine;
306         }
307         current = current.parent;
308     }
309     // not found
310
return null;
311 }
312
313 /*
314  * lookup through continue labels
315  */

316 public FlowContext getTargetContextForContinueLabel(char[] labelName) {
317     FlowContext current = this;
318     FlowContext lastContinuable = null;
319     FlowContext lastNonReturningSubRoutine = null;
320
321     while (current != null) {
322         if (current.isNonReturningContext()) {
323             lastNonReturningSubRoutine = current;
324         } else {
325             if (current.isContinuable()) {
326                 lastContinuable = current;
327             }
328         }
329         
330         char[] currentLabelName;
331         if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
332             ((LabeledStatement)current.associatedNode).bits |= ASTNode.LabelUsed;
333
334             // matching label found
335
if ((lastContinuable != null)
336                     && (current.associatedNode.concreteStatement() == lastContinuable.associatedNode)) {
337                 
338                 if (lastNonReturningSubRoutine == null) return lastContinuable;
339                 return lastNonReturningSubRoutine;
340             }
341             // label is found, but not a continuable location
342
return FlowContext.NotContinuableContext;
343         }
344         current = current.parent;
345     }
346     // not found
347
return null;
348 }
349
350 /*
351  * lookup a default break through breakable locations
352  */

353 public FlowContext getTargetContextForDefaultBreak() {
354     FlowContext current = this, lastNonReturningSubRoutine = null;
355     while (current != null) {
356         if (current.isNonReturningContext()) {
357             lastNonReturningSubRoutine = current;
358         }
359         if (current.isBreakable() && current.labelName() == null) {
360             if (lastNonReturningSubRoutine == null) return current;
361             return lastNonReturningSubRoutine;
362         }
363         current = current.parent;
364     }
365     // not found
366
return null;
367 }
368
369 /*
370  * lookup a default continue amongst continuable locations
371  */

372 public FlowContext getTargetContextForDefaultContinue() {
373     FlowContext current = this, lastNonReturningSubRoutine = null;
374     while (current != null) {
375         if (current.isNonReturningContext()) {
376             lastNonReturningSubRoutine = current;
377         }
378         if (current.isContinuable()) {
379             if (lastNonReturningSubRoutine == null)
380                 return current;
381             return lastNonReturningSubRoutine;
382         }
383         current = current.parent;
384     }
385     // not found
386
return null;
387 }
388
389 public String JavaDoc individualToString() {
390     return "Flow context"; //$NON-NLS-1$
391
}
392
393 public FlowInfo initsOnBreak() {
394     return FlowInfo.DEAD_END;
395 }
396
397 public UnconditionalFlowInfo initsOnReturn() {
398     return FlowInfo.DEAD_END;
399 }
400
401 public boolean isBreakable() {
402     return false;
403 }
404
405 public boolean isContinuable() {
406     return false;
407 }
408
409 public boolean isNonReturningContext() {
410     return false;
411 }
412
413 public boolean isSubRoutine() {
414     return false;
415 }
416
417 public char[] labelName() {
418     return null;
419 }
420
421 public void recordBreakFrom(FlowInfo flowInfo) {
422     // default implementation: do nothing
423
}
424
425 public void recordBreakTo(FlowContext targetContext) {
426     // default implementation: do nothing
427
}
428
429 public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) {
430     // default implementation: do nothing
431
}
432
433 protected boolean recordFinalAssignment(VariableBinding variable, Reference finalReference) {
434     return true; // keep going
435
}
436
437 /**
438  * Record a null reference for use by deferred checks. Only looping or
439  * finally contexts really record that information.
440  * @param local the local variable involved in the check
441  * @param expression the expression within which local lays
442  * @param status the status against which the check must be performed; one of
443  * {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL
444  * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL},
445  * {@link #CAN_ONLY_NON_NULL CAN_ONLY_NON_NULL}, potentially
446  * combined with a context indicator (one of {@link #IN_COMPARISON_NULL},
447  * {@link #IN_COMPARISON_NON_NULL}, {@link #IN_ASSIGNMENT} or {@link #IN_INSTANCEOF})
448  */

449 protected void recordNullReference(LocalVariableBinding local,
450     Expression expression, int status) {
451     // default implementation: do nothing
452
}
453
454 public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
455     // default implementation: do nothing
456
}
457
458 public void recordSettingFinal(VariableBinding variable, Reference finalReference, FlowInfo flowInfo) {
459     if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
460     // for initialization inside looping statement that effectively loops
461
FlowContext context = this;
462     while (context != null) {
463         if (!context.recordFinalAssignment(variable, finalReference)) {
464             break; // no need to keep going
465
}
466         context = context.parent;
467     }
468     }
469 }
470
471 public static final int
472   CAN_ONLY_NULL_NON_NULL = 0x0000,
473     // check against null and non null, with definite values -- comparisons
474
CAN_ONLY_NULL = 0x0001,
475     // check against null, with definite values -- comparisons
476
CAN_ONLY_NON_NULL = 0x0002,
477     // check against non null, with definite values -- comparisons
478
MAY_NULL = 0x0003,
479     // check against null, with potential values -- NPE guard
480
CHECK_MASK = 0x00FF,
481   IN_COMPARISON_NULL = 0x0100,
482   IN_COMPARISON_NON_NULL = 0x0200,
483     // check happened in a comparison
484
IN_ASSIGNMENT = 0x0300,
485     // check happened in an assignment
486
IN_INSTANCEOF = 0x0400,
487     // check happened in an instanceof expression
488
CONTEXT_MASK = ~CHECK_MASK;
489
490 /**
491  * Record a null reference for use by deferred checks. Only looping or
492  * finally contexts really record that information. The context may
493  * emit an error immediately depending on the status of local against
494  * flowInfo and its nature (only looping of finally contexts defer part
495  * of the checks; nonetheless, contexts that are nested into a looping or a
496  * finally context get affected and delegate some checks to their enclosing
497  * context).
498  * @param scope the scope into which the check is performed
499  * @param local the local variable involved in the check
500  * @param reference the expression within which local lies
501  * @param checkType the status against which the check must be performed; one
502  * of {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL
503  * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL}, potentially
504  * combined with a context indicator (one of {@link #IN_COMPARISON_NULL},
505  * {@link #IN_COMPARISON_NON_NULL}, {@link #IN_ASSIGNMENT} or {@link #IN_INSTANCEOF})
506  * @param flowInfo the flow info at the check point; deferring contexts will
507  * perform supplementary checks against flow info instances that cannot
508  * be known at the time of calling this method (they are influenced by
509  * code that follows the current point)
510  */

511 public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
512         Expression reference, int checkType, FlowInfo flowInfo) {
513     if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
514             flowInfo.isDefinitelyUnknown(local)) {
515         return;
516     }
517     switch (checkType) {
518         case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
519         case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
520             if (flowInfo.isDefinitelyNonNull(local)) {
521                 if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
522                     scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference);
523                 } else {
524                     scope.problemReporter().localVariableNonNullComparedToNull(local, reference);
525                 }
526                 return;
527             }
528             else if (flowInfo.cannotBeDefinitelyNullOrNonNull(local)) {
529                 return;
530             }
531         case CAN_ONLY_NULL | IN_COMPARISON_NULL:
532         case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
533         case CAN_ONLY_NULL | IN_ASSIGNMENT:
534         case CAN_ONLY_NULL | IN_INSTANCEOF:
535             if (flowInfo.isDefinitelyNull(local)) {
536                 switch(checkType & CONTEXT_MASK) {
537                     case FlowContext.IN_COMPARISON_NULL:
538                         scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
539                         return;
540                     case FlowContext.IN_COMPARISON_NON_NULL:
541                         scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
542                         return;
543                     case FlowContext.IN_ASSIGNMENT:
544                         scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
545                         return;
546                     case FlowContext.IN_INSTANCEOF:
547                         scope.problemReporter().localVariableNullInstanceof(local, reference);
548                         return;
549                 }
550             } else if (flowInfo.cannotBeDefinitelyNullOrNonNull(local)) {
551                 return;
552             }
553             break;
554         case MAY_NULL :
555             if (flowInfo.isDefinitelyNull(local)) {
556                 scope.problemReporter().localVariableNullReference(local, reference);
557                 return;
558             }
559             if (flowInfo.isPotentiallyNull(local)) {
560                 scope.problemReporter().localVariablePotentialNullReference(local, reference);
561                 return;
562             }
563             break;
564         default:
565             // never happens
566
}
567     if (this.parent != null) {
568         this.parent.recordUsingNullReference(scope, local, reference, checkType,
569                 flowInfo);
570     }
571 }
572
573 void removeFinalAssignmentIfAny(Reference reference) {
574     // default implementation: do nothing
575
}
576
577 public SubRoutineStatement subroutine() {
578     return null;
579 }
580
581 public String JavaDoc toString() {
582     StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
583     FlowContext current = this;
584     int parentsCount = 0;
585     while ((current = current.parent) != null) {
586         parentsCount++;
587     }
588     FlowContext[] parents = new FlowContext[parentsCount + 1];
589     current = this;
590     int index = parentsCount;
591     while (index >= 0) {
592         parents[index--] = current;
593         current = current.parent;
594     }
595     for (int i = 0; i < parentsCount; i++) {
596         for (int j = 0; j < i; j++)
597             buffer.append('\t');
598         buffer.append(parents[i].individualToString()).append('\n');
599     }
600     buffer.append('*');
601     for (int j = 0; j < parentsCount + 1; j++)
602         buffer.append('\t');
603     buffer.append(individualToString()).append('\n');
604     return buffer.toString();
605 }
606 }
607
Popular Tags