added -J option to preserve unmodified files in preexisting jarfile
[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.Expression;
17 import org.eclipse.jdt.internal.compiler.ast.Reference;
18 import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
19 import org.eclipse.jdt.internal.compiler.ast.TryStatement;
20 import org.eclipse.jdt.internal.compiler.codegen.Label;
21 import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
22 import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
23 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
24 import org.eclipse.jdt.internal.compiler.lookup.Scope;
25 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
26 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
27 import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
28
29 /**
30  * Reflects the context of code analysis, keeping track of enclosing
31  *      try statements, exception handlers, etc...
32  */
33 public class FlowContext implements TypeConstants {
34         
35         public ASTNode associatedNode;
36         public FlowContext parent;
37
38         public final static FlowContext NotContinuableContext = new FlowContext(null, null);
39                 
40         public FlowContext(FlowContext parent, ASTNode associatedNode) {
41
42                 this.parent = parent;
43                 this.associatedNode = associatedNode;
44         }
45         
46         public Label breakLabel() {
47
48                 return null;
49         }
50         
51         public void checkExceptionHandlers(
52                 TypeBinding[] raisedExceptions,
53                 ASTNode location,
54                 FlowInfo flowInfo,
55                 BlockScope scope) {
56
57                 // check that all the argument exception types are handled
58                 // JDK Compatible implementation - when an exception type is thrown, 
59                 // all related catch blocks are marked as reachable... instead of those only
60                 // until the point where it is safely handled (Smarter - see comment at the end)
61                 int remainingCount; // counting the number of remaining unhandled exceptions
62                 int raisedCount; // total number of exceptions raised
63                 if ((raisedExceptions == null)
64                         || ((raisedCount = raisedExceptions.length) == 0))
65                         return;
66                 remainingCount = raisedCount;
67
68                 // duplicate the array of raised exceptions since it will be updated
69                 // (null replaces any handled exception)
70                 System.arraycopy(
71                         raisedExceptions,
72                         0,
73                         (raisedExceptions = new TypeBinding[raisedCount]),
74                         0,
75                         raisedCount);
76                 FlowContext traversedContext = this;
77
78                 while (traversedContext != null) {
79                         SubRoutineStatement sub;
80                         if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
81                                 // traversing a non-returning subroutine means that all unhandled 
82                                 // exceptions will actually never get sent...
83                                 return;
84                         }
85                         // filter exceptions that are locally caught from the innermost enclosing 
86                         // try statement to the outermost ones.
87                         if (traversedContext instanceof ExceptionHandlingFlowContext) {
88                                 ExceptionHandlingFlowContext exceptionContext =
89                                         (ExceptionHandlingFlowContext) traversedContext;
90                                 ReferenceBinding[] caughtExceptions;
91                                 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
92                                         int caughtCount = caughtExceptions.length;
93                                         boolean[] locallyCaught = new boolean[raisedCount]; // at most
94
95                                         for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
96                                                 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
97                                                 for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
98                                                         TypeBinding raisedException;
99                                                         if ((raisedException = raisedExceptions[raisedIndex]) != null) {
100                                                             int state = caughtException == null 
101                                                                 ? EqualOrMoreSpecific /* any exception */
102                                                                 : Scope.compareTypes(raisedException, caughtException);
103                                                                 switch (state) {
104                                                                         case EqualOrMoreSpecific :
105                                                                                 exceptionContext.recordHandlingException(
106                                                                                         caughtException,
107                                                                                         flowInfo.unconditionalInits(),
108                                                                                         raisedException,
109                                                                                         location,
110                                                                                         locallyCaught[raisedIndex]);
111                                                                                 // was already definitely caught ?
112                                                                                 if (!locallyCaught[raisedIndex]) {
113                                                                                         locallyCaught[raisedIndex] = true;
114                                                                                         // remember that this exception has been definitely caught
115                                                                                         remainingCount--;
116                                                                                 }
117                                                                                 break;
118                                                                         case MoreGeneric :
119                                                                                 exceptionContext.recordHandlingException(
120                                                                                         caughtException,
121                                                                                         flowInfo.unconditionalInits(),
122                                                                                         raisedException,
123                                                                                         location,
124                                                                                         false);
125                                                                                 // was not caught already per construction
126                                                                 }
127                                                         }
128                                                 }
129                                         }
130                                         // remove locally caught exceptions from the remaining ones
131                                         for (int i = 0; i < raisedCount; i++) {
132                                                 if (locallyCaught[i]) {
133                                                         raisedExceptions[i] = null; // removed from the remaining ones.
134                                                 }
135                                         }
136                                 }
137                                 // method treatment for unchecked exceptions
138                                 if (exceptionContext.isMethodContext) {
139                                         for (int i = 0; i < raisedCount; i++) {
140                                                 TypeBinding raisedException;
141                                                 if ((raisedException = raisedExceptions[i]) != null) {
142                                                         if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
143                                                                 || raisedException.isCompatibleWith(scope.getJavaLangError())) {
144                                                                 remainingCount--;
145                                                                 raisedExceptions[i] = null;
146                                                         }
147                                                 }
148                                         }
149                                         // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
150                                         // clause will be fixed up later as per JLS 8.6).
151                                         if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
152                                                 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
153                                                 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
154                                                                 
155                                                         for (int i = 0; i < raisedCount; i++) {
156                                                                 TypeBinding raisedException;
157                                                                 if ((raisedException = raisedExceptions[i]) != null) {
158                                                                         exceptionContext.mergeUnhandledException(raisedException);
159                                                                 }
160                                                         }
161                                                         return; // no need to complain, will fix up constructor exceptions                                              
162                                                 }
163                                         }
164                                         break; // not handled anywhere, thus jump to error handling
165                                 }
166                         }
167                         if (remainingCount == 0)
168                                 return;
169                                 
170                         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
171                         if (traversedContext.associatedNode instanceof TryStatement){
172                                 TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
173                                 flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits);
174                         }
175                         traversedContext = traversedContext.parent;
176                 }
177                 // if reaches this point, then there are some remaining unhandled exception types.      
178                 nextReport: for (int i = 0; i < raisedCount; i++) {
179                         TypeBinding exception;
180                         if ((exception = raisedExceptions[i]) != null) {
181                                 // only one complaint if same exception declared to be thrown more than once
182                                 for (int j = 0; j < i; j++) {
183                                         if (raisedExceptions[j] == exception) continue nextReport; // already reported 
184                                 }
185                                 scope.problemReporter().unhandledException(exception, location);
186                         }
187                 }
188         }
189
190         public void checkExceptionHandlers(
191                 TypeBinding raisedException,
192                 ASTNode location,
193                 FlowInfo flowInfo,
194                 BlockScope scope) {
195
196                 // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
197                 // check that all the argument exception types are handled
198                 // JDK Compatible implementation - when an exception type is thrown, 
199                 // all related catch blocks are marked as reachable... instead of those only
200                 // until the point where it is safely handled (Smarter - see comment at the end)
201                 FlowContext traversedContext = this;
202                 while (traversedContext != null) {
203                         SubRoutineStatement sub;
204                         if (((sub = traversedContext.subRoutine()) != null) && sub.isSubRoutineEscaping()) {
205                                 // traversing a non-returning subroutine means that all unhandled 
206                                 // exceptions will actually never get sent...
207                                 return;
208                         }
209                         
210                         // filter exceptions that are locally caught from the innermost enclosing 
211                         // try statement to the outermost ones.
212                         if (traversedContext instanceof ExceptionHandlingFlowContext) {
213                                 ExceptionHandlingFlowContext exceptionContext =
214                                         (ExceptionHandlingFlowContext) traversedContext;
215                                 ReferenceBinding[] caughtExceptions;
216                                 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
217                                         boolean definitelyCaught = false;
218                                         for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
219                                                 caughtIndex < caughtCount;
220                                                 caughtIndex++) {
221                                                 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
222                                             int state = caughtException == null 
223                                                 ? EqualOrMoreSpecific /* any exception */
224                                                 : Scope.compareTypes(raisedException, caughtException);                                         
225                                                 switch (state) {
226                                                         case EqualOrMoreSpecific :
227                                                                 exceptionContext.recordHandlingException(
228                                                                         caughtException,
229                                                                         flowInfo.unconditionalInits(),
230                                                                         raisedException,
231                                                                         location,
232                                                                         definitelyCaught);
233                                                                 // was it already definitely caught ?
234                                                                 definitelyCaught = true;
235                                                                 break;
236                                                         case MoreGeneric :
237                                                                 exceptionContext.recordHandlingException(
238                                                                         caughtException,
239                                                                         flowInfo.unconditionalInits(),
240                                                                         raisedException,
241                                                                         location,
242                                                                         false);
243                                                                 // was not caught already per construction
244                                                 }
245                                         }
246                                         if (definitelyCaught)
247                                                 return;
248                                 }
249                                 // method treatment for unchecked exceptions
250                                 if (exceptionContext.isMethodContext) {
251                                         if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
252                                                 || raisedException.isCompatibleWith(scope.getJavaLangError()))
253                                                 return;
254                                                 
255                                         // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
256                                         // clause will be fixed up later as per JLS 8.6).
257                                         if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
258                                                 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
259                                                 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
260                                                                         
261                                                         exceptionContext.mergeUnhandledException(raisedException);
262                                                         return; // no need to complain, will fix up constructor exceptions                                              
263                                                 }
264                                         }
265                                         break; // not handled anywhere, thus jump to error handling
266                                 }
267                         }
268
269                         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
270                         if (traversedContext.associatedNode instanceof TryStatement){
271                                 TryStatement tryStatement = (TryStatement) traversedContext.associatedNode;
272                                 flowInfo = flowInfo.copy().addInitializationsFrom(tryStatement.subRoutineInits);
273                         }
274                         traversedContext = traversedContext.parent;
275                 }
276                 // if reaches this point, then there are some remaining unhandled exception types.
277                 scope.problemReporter().unhandledException(raisedException, location);
278         }
279
280         public Label continueLabel() {
281
282                 return null;
283         }
284
285         /*
286          * lookup through break labels
287          */
288         public FlowContext getTargetContextForBreakLabel(char[] labelName) {
289
290                 FlowContext current = this, lastNonReturningSubRoutine = null;
291                 while (current != null) {
292                         if (current.isNonReturningContext()) {
293                                 lastNonReturningSubRoutine = current;
294                         }
295                         char[] currentLabelName;
296                         if (((currentLabelName = current.labelName()) != null)
297                                 && CharOperation.equals(currentLabelName, labelName)) {
298                                 if (lastNonReturningSubRoutine == null)
299                                         return current;
300                                 return lastNonReturningSubRoutine;
301                         }
302                         current = current.parent;
303                 }
304                 // not found
305                 return null;
306         }
307
308         /*
309          * lookup through continue labels
310          */
311         public FlowContext getTargetContextForContinueLabel(char[] labelName) {
312
313                 FlowContext current = this;
314                 FlowContext lastContinuable = null;
315                 FlowContext lastNonReturningSubRoutine = null;
316
317                 while (current != null) {
318                         if (current.isNonReturningContext()) {
319                                 lastNonReturningSubRoutine = current;
320                         } else {
321                                 if (current.isContinuable()) {
322                                         lastContinuable = current;
323                                 }
324                         }
325                         
326                         char[] currentLabelName;
327                         if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
328
329                                 // matching label found                                 
330                                 if ((lastContinuable != null)
331                                                 && (current.associatedNode.concreteStatement()  == lastContinuable.associatedNode)) {
332                                     
333                                         if (lastNonReturningSubRoutine == null) return lastContinuable;
334                                         return lastNonReturningSubRoutine;
335                                 } 
336                                 // label is found, but not a continuable location
337                                 return NotContinuableContext;
338                         }
339                         current = current.parent;
340                 }
341                 // not found
342                 return null;
343         }
344
345         /*
346          * lookup a default break through breakable locations
347          */
348         public FlowContext getTargetContextForDefaultBreak() {
349
350                 FlowContext current = this, lastNonReturningSubRoutine = null;
351                 while (current != null) {
352                         if (current.isNonReturningContext()) {
353                                 lastNonReturningSubRoutine = current;
354                         }
355                         if (current.isBreakable() && current.labelName() == null) {
356                                 if (lastNonReturningSubRoutine == null) return current;
357                                 return lastNonReturningSubRoutine;
358                         }
359                         current = current.parent;
360                 }
361                 // not found
362                 return null;
363         }
364
365         /*
366          * lookup a default continue amongst continuable locations
367          */
368         public FlowContext getTargetContextForDefaultContinue() {
369
370                 FlowContext current = this, lastNonReturningSubRoutine = null;
371                 while (current != null) {
372                         if (current.isNonReturningContext()) {
373                                 lastNonReturningSubRoutine = current;
374                         }
375                         if (current.isContinuable()) {
376                                 if (lastNonReturningSubRoutine == null)
377                                         return current;
378                                 return lastNonReturningSubRoutine;
379                         }
380                         current = current.parent;
381                 }
382                 // not found
383                 return null;
384         }
385
386         public String individualToString() {
387
388                 return "Flow context"; //$NON-NLS-1$
389         }
390
391         public FlowInfo initsOnBreak() {
392
393                 return FlowInfo.DEAD_END;
394         }
395
396         public UnconditionalFlowInfo initsOnReturn() {
397
398                 return FlowInfo.DEAD_END;
399         }
400
401         public boolean isBreakable() {
402
403                 return false;
404         }
405
406         public boolean isContinuable() {
407
408                 return false;
409         }
410
411         public boolean isNonReturningContext() {
412
413                 return false;
414         }
415
416         public boolean isSubRoutine() {
417
418                 return false;
419         }
420
421         public char[] labelName() {
422
423                 return null;
424         }
425
426         public void recordBreakFrom(FlowInfo flowInfo) {
427                 // default implementation: do nothing
428         }
429
430         public void recordContinueFrom(FlowInfo flowInfo) {
431                 // default implementation: do nothing
432         }
433
434         protected boolean recordFinalAssignment(
435                 VariableBinding variable,
436                 Reference finalReference) {
437
438                 return true; // keep going
439         }
440
441         protected boolean recordNullReference(Expression expression, int status) {
442
443                 return false; // keep going
444         }
445         
446         public void recordReturnFrom(FlowInfo flowInfo) {
447                 // default implementation: do nothing
448         }
449
450         public void recordSettingFinal(
451                 VariableBinding variable,
452                 Reference finalReference,
453                 FlowInfo flowInfo) {
454
455                 if (!flowInfo.isReachable()) return;
456
457                 // for initialization inside looping statement that effectively loops
458                 FlowContext context = this;
459                 while (context != null) {
460                         if (!context.recordFinalAssignment(variable, finalReference)) {
461                                 break; // no need to keep going
462                         }
463                         context = context.parent;
464                 }
465         }
466
467         public void recordUsingNullReference(Scope scope, LocalVariableBinding local, Expression reference, int status, FlowInfo flowInfo) {
468
469                 if (!flowInfo.isReachable()) return;
470
471                 switch (status) {
472                         case FlowInfo.NULL :
473                                 if (flowInfo.isDefinitelyNull(local)) {
474                                         scope.problemReporter().localVariableCanOnlyBeNull(local, reference);
475                                         return;
476                                 } else if (flowInfo.isDefinitelyNonNull(local)) {
477                                         scope.problemReporter().localVariableCannotBeNull(local, reference);                            
478                                         return;
479                                 }
480                                 break;
481                         case FlowInfo.NON_NULL :
482                                 if (flowInfo.isDefinitelyNull(local)) {
483                                         scope.problemReporter().localVariableCanOnlyBeNull(local, reference);                           
484                                         return;
485                                 }
486                                 break;
487                 }
488                 
489                 // for initialization inside looping statement that effectively loops
490                 FlowContext context = this;
491                 while (context != null) {
492                         if (context.recordNullReference(reference, status)) {
493                                 return; // no need to keep going
494                         }
495                         context = context.parent;
496                 }
497         }
498         
499         void removeFinalAssignmentIfAny(Reference reference) {
500                 // default implementation: do nothing
501         }
502
503         public SubRoutineStatement subRoutine() {
504
505                 return null;
506         }
507
508         public String toString() {
509
510                 StringBuffer buffer = new StringBuffer();
511                 FlowContext current = this;
512                 int parentsCount = 0;
513                 while ((current = current.parent) != null) {
514                         parentsCount++;
515                 }
516                 FlowContext[] parents = new FlowContext[parentsCount + 1];
517                 current = this;
518                 int index = parentsCount;
519                 while (index >= 0) {
520                         parents[index--] = current;
521                         current = current.parent;
522                 }
523                 for (int i = 0; i < parentsCount; i++) {
524                         for (int j = 0; j < i; j++)
525                                 buffer.append('\t');
526                         buffer.append(parents[i].individualToString()).append('\n');
527                 }
528                 buffer.append('*');
529                 for (int j = 0; j < parentsCount + 1; j++)
530                         buffer.append('\t');
531                 buffer.append(individualToString()).append('\n');
532                 return buffer.toString();
533         }
534 }