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