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