changes for VariableHover
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / flow / FlowContext.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.flow;
12
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
15 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
16 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
17 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
18 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.TypeConstants;
20 import net.sourceforge.phpdt.internal.compiler.lookup.VariableBinding;
21 import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
22 import net.sourceforge.phpeclipse.internal.compiler.ast.AstNode;
23 import net.sourceforge.phpeclipse.internal.compiler.ast.Reference;
24 import net.sourceforge.phpeclipse.internal.compiler.ast.TryStatement;
25
26 /**
27  * Reflects the context of code analysis, keeping track of enclosing
28  *      try statements, exception handlers, etc...
29  */
30 public class FlowContext implements TypeConstants {
31         
32         public AstNode associatedNode;
33         public FlowContext parent;
34
35         public final static FlowContext NotContinuableContext = new FlowContext(null, null);
36                 
37         public FlowContext(FlowContext parent, AstNode associatedNode) {
38
39                 this.parent = parent;
40                 this.associatedNode = associatedNode;
41         }
42         
43         public Label breakLabel() {
44
45                 return null;
46         }
47         
48         public void checkExceptionHandlers(
49                 TypeBinding[] raisedExceptions,
50                 AstNode location,
51                 FlowInfo flowInfo,
52                 BlockScope scope) {
53
54                 // check that all the argument exception types are handled
55                 // JDK Compatible implementation - when an exception type is thrown, 
56                 // all related catch blocks are marked as reachable... instead of those only
57                 // until the point where it is safely handled (Smarter - see comment at the end)
58                 int remainingCount; // counting the number of remaining unhandled exceptions
59                 int raisedCount; // total number of exceptions raised
60                 if ((raisedExceptions == null)
61                         || ((raisedCount = raisedExceptions.length) == 0))
62                         return;
63                 remainingCount = raisedCount;
64
65                 // duplicate the array of raised exceptions since it will be updated
66                 // (null replaces any handled exception)
67                 System.arraycopy(
68                         raisedExceptions,
69                         0,
70                         (raisedExceptions = new TypeBinding[raisedCount]),
71                         0,
72                         raisedCount);
73                 FlowContext traversedContext = this;
74
75                 while (traversedContext != null) {
76                         AstNode sub;
77                         if (((sub = traversedContext.subRoutine()) != null) && sub.cannotReturn()) {
78                                 // traversing a non-returning subroutine means that all unhandled 
79                                 // exceptions will actually never get sent...
80                                 return;
81                         }
82                         // filter exceptions that are locally caught from the innermost enclosing 
83                         // try statement to the outermost ones.
84                         if (traversedContext instanceof ExceptionHandlingFlowContext) {
85                                 ExceptionHandlingFlowContext exceptionContext =
86                                         (ExceptionHandlingFlowContext) traversedContext;
87                                 ReferenceBinding[] caughtExceptions;
88                                 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
89                                         int caughtCount = caughtExceptions.length;
90                                         boolean[] locallyCaught = new boolean[raisedCount]; // at most
91
92                                         for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
93                                                 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
94                                                 for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
95                                                         TypeBinding raisedException;
96                                                         if ((raisedException = raisedExceptions[raisedIndex]) != null) {
97                                                                 switch (Scope.compareTypes(raisedException, caughtException)) {
98                                                                         case EqualOrMoreSpecific :
99                                                                                 exceptionContext.recordHandlingException(
100                                                                                         caughtException,
101                                                                                         flowInfo.unconditionalInits(),
102                                                                                         raisedException,
103                                                                                         location,
104                                                                                         locallyCaught[raisedIndex]);
105                                                                                 // was already definitely caught ?
106                                                                                 if (!locallyCaught[raisedIndex]) {
107                                                                                         locallyCaught[raisedIndex] = true;
108                                                                                         // remember that this exception has been definitely caught
109                                                                                         remainingCount--;
110                                                                                 }
111                                                                                 break;
112                                                                         case MoreGeneric :
113                                                                                 exceptionContext.recordHandlingException(
114                                                                                         caughtException,
115                                                                                         flowInfo.unconditionalInits(),
116                                                                                         raisedException,
117                                                                                         location,
118                                                                                         false);
119                                                                                 // was not caught already per construction
120                                                                 }
121                                                         }
122                                                 }
123                                         }
124                                         // remove locally caught exceptions from the remaining ones
125                                         for (int i = 0; i < raisedCount; i++) {
126                                                 if (locallyCaught[i]) {
127                                                         raisedExceptions[i] = null; // removed from the remaining ones.
128                                                 }
129                                         }
130                                 }
131                                 // method treatment for unchecked exceptions
132                                 if (exceptionContext.isMethodContext) {
133                                         for (int i = 0; i < raisedCount; i++) {
134                                                 TypeBinding raisedException;
135                                                 if ((raisedException = raisedExceptions[i]) != null) {
136                                                         if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
137                                                                 || raisedException.isCompatibleWith(scope.getJavaLangError())) {
138                                                                 remainingCount--;
139                                                                 raisedExceptions[i] = null;
140                                                         }
141                                                 }
142                                         }
143                                         // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
144                                         // clause will be fixed up later as per JLS 8.6).
145                                         if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
146                                                 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
147                                                 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
148                                                                 
149                                                         for (int i = 0; i < raisedCount; i++) {
150                                                                 TypeBinding raisedException;
151                                                                 if ((raisedException = raisedExceptions[i]) != null) {
152                                                                         exceptionContext.mergeUnhandledException(raisedException);
153                                                                 }
154                                                         }
155                                                         return; // no need to complain, will fix up constructor exceptions                                              
156                                                 }
157                                         }
158                                         break; // not handled anywhere, thus jump to error handling
159                                 }
160                         }
161                         if (remainingCount == 0)
162                                 return;
163                                 
164                         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
165                         if (traversedContext.associatedNode instanceof TryStatement){
166                                 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
167                         }
168                         traversedContext = traversedContext.parent;
169                 }
170                 // if reaches this point, then there are some remaining unhandled exception types.      
171                 nextReport: for (int i = 0; i < raisedCount; i++) {
172                         TypeBinding exception;
173                         if ((exception = raisedExceptions[i]) != null) {
174                                 // only one complaint if same exception declared to be thrown more than once
175                                 for (int j = 0; j < i; j++) {
176                                         if (raisedExceptions[j] == exception) continue nextReport; // already reported 
177                                 }
178                                 scope.problemReporter().unhandledException(exception, location);
179                         }
180                 }
181         }
182
183         public void checkExceptionHandlers(
184                 TypeBinding raisedException,
185                 AstNode location,
186                 FlowInfo flowInfo,
187                 BlockScope scope) {
188
189                 // LIGHT-VERSION OF THE EQUIVALENT WITH AN ARRAY OF EXCEPTIONS
190                 // check that all the argument exception types are handled
191                 // JDK Compatible implementation - when an exception type is thrown, 
192                 // all related catch blocks are marked as reachable... instead of those only
193                 // until the point where it is safely handled (Smarter - see comment at the end)
194                 FlowContext traversedContext = this;
195                 while (traversedContext != null) {
196                         AstNode sub;
197                         if (((sub = traversedContext.subRoutine()) != null) && sub.cannotReturn()) {
198                                 // traversing a non-returning subroutine means that all unhandled 
199                                 // exceptions will actually never get sent...
200                                 return;
201                         }
202                         
203                         // filter exceptions that are locally caught from the innermost enclosing 
204                         // try statement to the outermost ones.
205                         if (traversedContext instanceof ExceptionHandlingFlowContext) {
206                                 ExceptionHandlingFlowContext exceptionContext =
207                                         (ExceptionHandlingFlowContext) traversedContext;
208                                 ReferenceBinding[] caughtExceptions;
209                                 if ((caughtExceptions = exceptionContext.handledExceptions) != NoExceptions) {
210                                         boolean definitelyCaught = false;
211                                         for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
212                                                 caughtIndex < caughtCount;
213                                                 caughtIndex++) {
214                                                 ReferenceBinding caughtException = caughtExceptions[caughtIndex];
215                                                 switch (Scope.compareTypes(raisedException, caughtException)) {
216                                                         case EqualOrMoreSpecific :
217                                                                 exceptionContext.recordHandlingException(
218                                                                         caughtException,
219                                                                         flowInfo.unconditionalInits(),
220                                                                         raisedException,
221                                                                         location,
222                                                                         definitelyCaught);
223                                                                 // was it already definitely caught ?
224                                                                 definitelyCaught = true;
225                                                                 break;
226                                                         case MoreGeneric :
227                                                                 exceptionContext.recordHandlingException(
228                                                                         caughtException,
229                                                                         flowInfo.unconditionalInits(),
230                                                                         raisedException,
231                                                                         location,
232                                                                         false);
233                                                                 // was not caught already per construction
234                                                 }
235                                         }
236                                         if (definitelyCaught)
237                                                 return;
238                                 }
239                                 // method treatment for unchecked exceptions
240                                 if (exceptionContext.isMethodContext) {
241                                         if (raisedException.isCompatibleWith(scope.getJavaLangRuntimeException())
242                                                 || raisedException.isCompatibleWith(scope.getJavaLangError()))
243                                                 return;
244                                                 
245                                         // anonymous constructors are allowed to throw any exceptions (their thrown exceptions
246                                         // clause will be fixed up later as per JLS 8.6).
247                                         if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration){
248                                                 AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
249                                                 if (method.isConstructor() && method.binding.declaringClass.isAnonymousType()){
250                                                                         
251                                                         exceptionContext.mergeUnhandledException(raisedException);
252                                                         return; // no need to complain, will fix up constructor exceptions                                              
253                                                 }
254                                         }
255                                         break; // not handled anywhere, thus jump to error handling
256                                 }
257                         }
258
259                         traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
260                         if (traversedContext.associatedNode instanceof TryStatement){
261                                 flowInfo = flowInfo.copy().addInitializationsFrom(((TryStatement) traversedContext.associatedNode).subRoutineInits);
262                         }
263                         traversedContext = traversedContext.parent;
264                 }
265                 // if reaches this point, then there are some remaining unhandled exception types.
266                 scope.problemReporter().unhandledException(raisedException, location);
267         }
268
269         public Label continueLabel() {
270
271                 return null;
272         }
273
274         /*
275          * lookup through break labels
276          */
277         public FlowContext getTargetContextForBreakLabel(char[] labelName) {
278
279                 FlowContext current = this, lastNonReturningSubRoutine = null;
280                 while (current != null) {
281                         if (current.isNonReturningContext()) {
282                                 lastNonReturningSubRoutine = current;
283                         }
284                         char[] currentLabelName;
285                         if (((currentLabelName = current.labelName()) != null)
286                                 && CharOperation.equals(currentLabelName, labelName)) {
287                                 if (lastNonReturningSubRoutine == null) {
288                                         return current;
289                                 } else {
290                                         return lastNonReturningSubRoutine;
291                                 }
292                         }
293                         current = current.parent;
294                 }
295                 // not found
296                 return null;
297         }
298
299         /*
300          * lookup through continue labels
301          */
302         public FlowContext getTargetContextForContinueLabel(char[] labelName) {
303
304                 FlowContext current = this;
305                 FlowContext lastContinuable = null;
306                 FlowContext lastNonReturningSubRoutine = null;
307
308                 while (current != null) {
309                         if (current.isNonReturningContext()) {
310                                 lastNonReturningSubRoutine = current;
311                         } else {
312                                 if (current.isContinuable()) {
313                                         lastContinuable = current;
314                                 }
315                         }
316                         
317                         char[] currentLabelName;
318                         if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
319
320                                 // matching label found                                 
321                                 if ((lastContinuable != null)
322                                         && (current.associatedNode.concreteStatement()  == lastContinuable.associatedNode)) {
323                                                 
324                                         if (lastNonReturningSubRoutine == null) {
325                                                 return lastContinuable;
326                                         } else {
327                                                 return lastNonReturningSubRoutine;
328                                         }
329                                 } else {
330                                         // label is found, but not a continuable location
331                                         return NotContinuableContext;
332                                 }
333                         }
334                         current = current.parent;
335                 }
336                 // not found
337                 return null;
338         }
339
340         /*
341          * lookup a default break through breakable locations
342          */
343         public FlowContext getTargetContextForDefaultBreak() {
344
345                 FlowContext current = this, lastNonReturningSubRoutine = null;
346                 while (current != null) {
347                         if (current.isNonReturningContext()) {
348                                 lastNonReturningSubRoutine = current;
349                         }
350                         if (current.isBreakable() && current.labelName() == null) {
351                                 if (lastNonReturningSubRoutine == null) {
352                                         return current;
353                                 } else {
354                                         return lastNonReturningSubRoutine;
355                                 }
356                         }
357                         current = current.parent;
358                 }
359                 // not found
360                 return null;
361         }
362
363         /*
364          * lookup a default continue amongst continuable locations
365          */
366         public FlowContext getTargetContextForDefaultContinue() {
367
368                 FlowContext current = this, lastNonReturningSubRoutine = null;
369                 while (current != null) {
370                         if (current.isNonReturningContext()) {
371                                 lastNonReturningSubRoutine = current;
372                         }
373                         if (current.isContinuable()) {
374                                 if (lastNonReturningSubRoutine == null) {
375                                         return current;
376                                 } else {
377                                         return lastNonReturningSubRoutine;
378                                 }
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         }
428
429         public void recordContinueFrom(FlowInfo flowInfo) {
430         }
431
432         boolean recordFinalAssignment(
433                 VariableBinding variable,
434                 Reference finalReference) {
435
436                 return true; // keep going
437         }
438
439         public void recordReturnFrom(FlowInfo flowInfo) {
440         }
441
442         public void recordSettingFinal(
443                 VariableBinding variable,
444                 Reference finalReference) {
445
446                 // for initialization inside looping statement that effectively loops
447                 FlowContext context = this;
448                 while (context != null) {
449                         if (!context.recordFinalAssignment(variable, finalReference)) {
450                                 break; // no need to keep going
451                         }
452                         context = context.parent;
453                 }
454         }
455
456         void removeFinalAssignmentIfAny(Reference reference) {
457         }
458
459         public AstNode subRoutine() {
460
461                 return null;
462         }
463
464         public String toString() {
465
466                 StringBuffer buffer = new StringBuffer();
467                 FlowContext current = this;
468                 int parentsCount = 0;
469                 while ((current = current.parent) != null) {
470                         parentsCount++;
471                 }
472                 FlowContext[] parents = new FlowContext[parentsCount + 1];
473                 current = this;
474                 int index = parentsCount;
475                 while (index >= 0) {
476                         parents[index--] = current;
477                         current = current.parent;
478                 }
479                 for (int i = 0; i < parentsCount; i++) {
480                         for (int j = 0; j < i; j++)
481                                 buffer.append('\t');
482                         buffer.append(parents[i].individualToString()).append('\n');
483                 }
484                 buffer.append('*');
485                 for (int j = 0; j < parentsCount + 1; j++)
486                         buffer.append('\t');
487                 buffer.append(individualToString()).append('\n');
488                 return buffer.toString();
489         }
490 }