removed Makefile; lifted repo/org.ibex.tool/src/ to src/
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / flow / FlowContext.java
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
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.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;
26
27 /**
28  * Reflects the context of code analysis, keeping track of enclosing
29  *      try statements, exception handlers, etc...
30  */
31 public class FlowContext implements TypeConstants {
32         
33         public ASTNode associatedNode;
34         public FlowContext parent;
35
36         public final static FlowContext NotContinuableContext = new FlowContext(null, null);
37                 
38         public FlowContext(FlowContext parent, ASTNode associatedNode) {
39
40                 this.parent = parent;
41                 this.associatedNode = associatedNode;
42         }
43         
44         public Label breakLabel() {
45
46                 return null;
47         }
48         
49         public void checkExceptionHandlers(
50                 TypeBinding[] raisedExceptions,
51                 ASTNode location,
52                 FlowInfo flowInfo,
53                 BlockScope scope) {
54
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))
63                         return;
64                 remainingCount = raisedCount;
65
66                 // duplicate the array of raised exceptions since it will be updated
67                 // (null replaces any handled exception)
68                 System.arraycopy(
69                         raisedExceptions,
70                         0,
71                         (raisedExceptions = new TypeBinding[raisedCount]),
72                         0,
73                         raisedCount);
74                 FlowContext traversedContext = this;
75
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...
81                                 return;
82                         }
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
92
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);
101                                                                 switch (state) {
102                                                                         case EqualOrMoreSpecific :
103                                                                                 exceptionContext.recordHandlingException(
104                                                                                         caughtException,
105                                                                                         flowInfo.unconditionalInits(),
106                                                                                         raisedException,
107                                                                                         location,
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
113                                                                                         remainingCount--;
114                                                                                 }
115                                                                                 break;
116                                                                         case MoreGeneric :
117                                                                                 exceptionContext.recordHandlingException(
118                                                                                         caughtException,
119                                                                                         flowInfo.unconditionalInits(),
120                                                                                         raisedException,
121                                                                                         location,
122                                                                                         false);
123                                                                                 // was not caught already per construction
124                                                                 }
125                                                         }
126                                                 }
127                                         }
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.
132                                                 }
133                                         }
134                                 }
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())) {
142                                                                 remainingCount--;
143                                                                 raisedExceptions[i] = null;
144                                                         }
145                                                 }
146                                         }
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()){
152                                                                 
153                                                         for (int i = 0; i < raisedCount; i++) {
154                                                                 TypeBinding raisedException;
155                                                                 if ((raisedException = raisedExceptions[i]) != null) {
156                                                                         exceptionContext.mergeUnhandledException(raisedException);
157                                                                 }
158                                                         }
159                                                         return; // no need to complain, will fix up constructor exceptions                                              
160                                                 }
161                                         }
162                                         break; // not handled anywhere, thus jump to error handling
163                                 }
164                         }
165                         if (remainingCount == 0)
166                                 return;
167                                 
168                         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
169                         if (traversedContext.associatedNode instanceof TryStatement){
170                                 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
171                         }
172                         traversedContext = traversedContext.parent;
173                 }
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 
181                                 }
182                                 scope.problemReporter().unhandledException(exception, location);
183                         }
184                 }
185         }
186
187         public void checkExceptionHandlers(
188                 TypeBinding raisedException,
189                 ASTNode location,
190                 FlowInfo flowInfo,
191                 BlockScope scope) {
192
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...
204                                 return;
205                         }
206                         
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;
217                                                 caughtIndex++) {
218                                                 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
219                                             int state = caughtException == null 
220                                                 ? EqualOrMoreSpecific /* any exception */
221                                                 : Scope.compareTypes(raisedException, caughtException);                                         
222                                                 switch (state) {
223                                                         case EqualOrMoreSpecific :
224                                                                 exceptionContext.recordHandlingException(
225                                                                         caughtException,
226                                                                         flowInfo.unconditionalInits(),
227                                                                         raisedException,
228                                                                         location,
229                                                                         definitelyCaught);
230                                                                 // was it already definitely caught ?
231                                                                 definitelyCaught = true;
232                                                                 break;
233                                                         case MoreGeneric :
234                                                                 exceptionContext.recordHandlingException(
235                                                                         caughtException,
236                                                                         flowInfo.unconditionalInits(),
237                                                                         raisedException,
238                                                                         location,
239                                                                         false);
240                                                                 // was not caught already per construction
241                                                 }
242                                         }
243                                         if (definitelyCaught)
244                                                 return;
245                                 }
246                                 // method treatment for unchecked exceptions
247                                 if (exceptionContext.isMethodContext) {
248                                         if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
249                                                 || raisedException.isCompatibleWith(scope.getJavaLangError()))
250                                                 return;
251                                                 
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()){
257                                                                         
258                                                         exceptionContext.mergeUnhandledException(raisedException);
259                                                         return; // no need to complain, will fix up constructor exceptions                                              
260                                                 }
261                                         }
262                                         break; // not handled anywhere, thus jump to error handling
263                                 }
264                         }
265
266                         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
267                         if (traversedContext.associatedNode instanceof TryStatement){
268                                 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
269                         }
270                         traversedContext = traversedContext.parent;
271                 }
272                 // if reaches this point, then there are some remaining unhandled exception types.
273                 scope.problemReporter().unhandledException(raisedException, location);
274         }
275
276         public Label continueLabel() {
277
278                 return null;
279         }
280
281         /*
282          * lookup through break labels
283          */
284         public FlowContext getTargetContextForBreakLabel(char[] labelName) {
285
286                 FlowContext current = this, lastNonReturningSubRoutine = null;
287                 while (current != null) {
288                         if (current.isNonReturningContext()) {
289                                 lastNonReturningSubRoutine = current;
290                         }
291                         char[] currentLabelName;
292                         if (((currentLabelName = current.labelName()) != null)
293                                 && CharOperation.equals(currentLabelName, labelName)) {
294                                 if (lastNonReturningSubRoutine == null)
295                                         return current;
296                                 return lastNonReturningSubRoutine;
297                         }
298                         current = current.parent;
299                 }
300                 // not found
301                 return null;
302         }
303
304         /*
305          * lookup through continue labels
306          */
307         public FlowContext getTargetContextForContinueLabel(char[] labelName) {
308
309                 FlowContext current = this;
310                 FlowContext lastContinuable = null;
311                 FlowContext lastNonReturningSubRoutine = null;
312
313                 while (current != null) {
314                         if (current.isNonReturningContext()) {
315                                 lastNonReturningSubRoutine = current;
316                         } else {
317                                 if (current.isContinuable()) {
318                                         lastContinuable = current;
319                                 }
320                         }
321                         
322                         char[] currentLabelName;
323                         if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
324
325                                 // matching label found                                 
326                                 if ((lastContinuable != null)
327                                                 && (current.associatedNode.concreteStatement()  == lastContinuable.associatedNode)) {
328                                     
329                                         if (lastNonReturningSubRoutine == null) return lastContinuable;
330                                         return lastNonReturningSubRoutine;
331                                 } 
332                                 // label is found, but not a continuable location
333                                 return NotContinuableContext;
334                         }
335                         current = current.parent;
336                 }
337                 // not found
338                 return null;
339         }
340
341         /*
342          * lookup a default break through breakable locations
343          */
344         public FlowContext getTargetContextForDefaultBreak() {
345
346                 FlowContext current = this, lastNonReturningSubRoutine = null;
347                 while (current != null) {
348                         if (current.isNonReturningContext()) {
349                                 lastNonReturningSubRoutine = current;
350                         }
351                         if (current.isBreakable() && current.labelName() == null) {
352                                 if (lastNonReturningSubRoutine == null) return current;
353                                 return lastNonReturningSubRoutine;
354                         }
355                         current = current.parent;
356                 }
357                 // not found
358                 return null;
359         }
360
361         /*
362          * lookup a default continue amongst continuable locations
363          */
364         public FlowContext getTargetContextForDefaultContinue() {
365
366                 FlowContext current = this, lastNonReturningSubRoutine = null;
367                 while (current != null) {
368                         if (current.isNonReturningContext()) {
369                                 lastNonReturningSubRoutine = current;
370                         }
371                         if (current.isContinuable()) {
372                                 if (lastNonReturningSubRoutine == null)
373                                         return current;
374                                 return lastNonReturningSubRoutine;
375                         }
376                         current = current.parent;
377                 }
378                 // not found
379                 return null;
380         }
381
382         public String individualToString() {
383
384                 return "Flow context"; //$NON-NLS-1$
385         }
386
387         public FlowInfo initsOnBreak() {
388
389                 return FlowInfo.DEAD_END;
390         }
391
392         public UnconditionalFlowInfo initsOnReturn() {
393
394                 return FlowInfo.DEAD_END;
395         }
396
397         public boolean isBreakable() {
398
399                 return false;
400         }
401
402         public boolean isContinuable() {
403
404                 return false;
405         }
406
407         public boolean isNonReturningContext() {
408
409                 return false;
410         }
411
412         public boolean isSubRoutine() {
413
414                 return false;
415         }
416
417         public char[] labelName() {
418
419                 return null;
420         }
421
422         public void recordBreakFrom(FlowInfo flowInfo) {
423                 // default implementation: do nothing
424         }
425
426         public void recordContinueFrom(FlowInfo flowInfo) {
427                 // default implementation: do nothing
428         }
429
430         boolean recordFinalAssignment(
431                 VariableBinding variable,
432                 Reference finalReference) {
433
434                 return true; // keep going
435         }
436
437         public void recordReturnFrom(FlowInfo flowInfo) {
438                 // default implementation: do nothing
439         }
440
441         public void recordSettingFinal(
442                 VariableBinding variable,
443                 Reference finalReference,
444                 FlowInfo flowInfo) {
445
446                 if (!flowInfo.isReachable()) return;
447
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
453                         }
454                         context = context.parent;
455                 }
456         }
457
458         void removeFinalAssignmentIfAny(Reference reference) {
459                 // default implementation: do nothing
460         }
461
462         public SubRoutineStatement subRoutine() {
463
464                 return null;
465         }
466
467         public String toString() {
468
469                 StringBuffer buffer = new StringBuffer();
470                 FlowContext current = this;
471                 int parentsCount = 0;
472                 while ((current = current.parent) != null) {
473                         parentsCount++;
474                 }
475                 FlowContext[] parents = new FlowContext[parentsCount + 1];
476                 current = this;
477                 int index = parentsCount;
478                 while (index >= 0) {
479                         parents[index--] = current;
480                         current = current.parent;
481                 }
482                 for (int i = 0; i < parentsCount; i++) {
483                         for (int j = 0; j < i; j++)
484                                 buffer.append('\t');
485                         buffer.append(parents[i].individualToString()).append('\n');
486                 }
487                 buffer.append('*');
488                 for (int j = 0; j < parentsCount + 1; j++)
489                         buffer.append('\t');
490                 buffer.append(individualToString()).append('\n');
491                 return buffer.toString();
492         }
493 }