1) Fixed issue #714: PHP Parser bug with $this->oR.
[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 }