improved PHP parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / flow / ExceptionHandlingFlowContext.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 java.util.ArrayList;
14
15 import net.sourceforge.phpdt.internal.compiler.codegen.ObjectCache;
16 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
17 import net.sourceforge.phpdt.internal.compiler.lookup.CompilerModifiers;
18 import net.sourceforge.phpdt.internal.compiler.lookup.MethodScope;
19 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
20 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
21 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
22 import net.sourceforge.phpeclipse.internal.compiler.ast.ASTNode;
23 import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
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 ExceptionHandlingFlowContext extends FlowContext {
31         
32         public ReferenceBinding[] handledExceptions;
33         
34         public final static int BitCacheSize = 32; // 32 bits per int
35         int[] isReached;
36         int[] isNeeded;
37         UnconditionalFlowInfo[] initsOnExceptions;
38         ObjectCache indexes = new ObjectCache();
39         boolean isMethodContext;
40
41         public UnconditionalFlowInfo initsOnReturn;
42
43         // for dealing with anonymous constructor thrown exceptions
44         public ArrayList extendedExceptions;
45         
46         public ExceptionHandlingFlowContext(
47                 FlowContext parent,
48                 ASTNode associatedNode,
49                 ReferenceBinding[] handledExceptions,
50                 BlockScope scope,
51                 UnconditionalFlowInfo flowInfo) {
52
53                 super(parent, associatedNode);
54                 isMethodContext = scope == scope.methodScope();
55                 this.handledExceptions = handledExceptions;
56                 int count = handledExceptions.length, cacheSize = (count / BitCacheSize) + 1;
57                 this.isReached = new int[cacheSize]; // none is reached by default
58                 this.isNeeded = new int[cacheSize]; // none is needed by default
59                 this.initsOnExceptions = new UnconditionalFlowInfo[count];
60                 for (int i = 0; i < count; i++) {
61                         this.indexes.put(handledExceptions[i], i); // key type  -> value index
62                         boolean isUnchecked =
63                                 (scope.compareUncheckedException(handledExceptions[i]) != NotRelated);
64                         int cacheIndex = i / BitCacheSize, bitMask = 1 << (i % BitCacheSize);
65                         if (isUnchecked) {
66                                 isReached[cacheIndex] |= bitMask;
67                                 this.initsOnExceptions[i] = flowInfo.copy().unconditionalInits();
68                         } else {
69                                 this.initsOnExceptions[i] = FlowInfo.DEAD_END;
70                         }
71                 }
72                 System.arraycopy(this.isReached, 0, this.isNeeded, 0, cacheSize);
73                 this.initsOnReturn = FlowInfo.DEAD_END; 
74         }
75         
76         
77 //      public void complainIfUnusedExceptionHandlers(
78 //              ASTNode[] exceptionHandlers,
79 //              BlockScope scope,
80 //              TryStatement tryStatement) {
81 //              // report errors for unreachable exception handlers
82 //              for (int i = 0, count = handledExceptions.length; i < count; i++) {
83 //                      int index = indexes.get(handledExceptions[i]);
84 //                      int cacheIndex = index / BitCacheSize;
85 //                      int bitMask = 1 << (index % BitCacheSize);
86 //                      if ((isReached[cacheIndex] & bitMask) == 0) {
87 //                              scope.problemReporter().unreachableExceptionHandler(
88 //                                      handledExceptions[index],
89 //                                      exceptionHandlers[index]);
90 //                      } else {
91 //                              if ((isNeeded[cacheIndex] & bitMask) == 0) {
92 //                                      scope.problemReporter().maskedExceptionHandler(
93 //                                              handledExceptions[index],
94 //                                              exceptionHandlers[index]);
95 //                              }
96 //                      }
97 //              }
98 //              // will optimized out unnecessary catch block during code gen
99 //              tryStatement.preserveExceptionHandler = isNeeded;
100 //      }
101
102         public String individualToString() {
103                 
104                 StringBuffer buffer = new StringBuffer("Exception flow context"); //$NON-NLS-1$
105                 int length = handledExceptions.length;
106                 for (int i = 0; i < length; i++) {
107                         int cacheIndex = i / BitCacheSize;
108                         int bitMask = 1 << (i % BitCacheSize);
109                         buffer.append('[').append(handledExceptions[i].readableName());
110                         if ((isReached[cacheIndex] & bitMask) != 0) {
111                                 if ((isNeeded[cacheIndex] & bitMask) == 0) {
112                                         buffer.append("-masked"); //$NON-NLS-1$
113                                 } else {
114                                         buffer.append("-reached"); //$NON-NLS-1$
115                                 }
116                         } else {
117                                 buffer.append("-not reached"); //$NON-NLS-1$
118                         }
119                         buffer.append('-').append(initsOnExceptions[i].toString()).append(']');
120                 }
121                 buffer.append("[initsOnReturn -").append(initsOnReturn.toString()).append(']'); //$NON-NLS-1$
122                 return buffer.toString();
123         }
124
125         public UnconditionalFlowInfo initsOnException(ReferenceBinding exceptionType) {
126                 
127                 int index;
128                 if ((index = indexes.get(exceptionType)) < 0) {
129                         return FlowInfo.DEAD_END;
130                 }
131                 return initsOnExceptions[index];
132         }
133
134         public UnconditionalFlowInfo initsOnReturn(){
135                 return this.initsOnReturn;
136         }
137         
138         public void recordHandlingException(
139                 ReferenceBinding exceptionType,
140                 UnconditionalFlowInfo flowInfo,
141                 TypeBinding raisedException,
142                 ASTNode invocationSite,
143                 boolean wasAlreadyDefinitelyCaught) {
144                         
145                 int index = indexes.get(exceptionType);
146                 // if already flagged as being reached (unchecked exception handler)
147                 int cacheIndex = index / BitCacheSize;
148                 int bitMask = 1 << (index % BitCacheSize);
149                 if (!wasAlreadyDefinitelyCaught) {
150                         this.isNeeded[cacheIndex] |= bitMask;
151                 }
152                 this.isReached[cacheIndex] |= bitMask;
153                 
154                 initsOnExceptions[index] =
155                         initsOnExceptions[index] == FlowInfo.DEAD_END
156                                 ? flowInfo.copy().unconditionalInits()
157                                 : initsOnExceptions[index].mergedWith(flowInfo);
158         }
159         
160         public void recordReturnFrom(FlowInfo flowInfo) {
161
162                 if (!flowInfo.isReachable()) return; 
163                 if (initsOnReturn == FlowInfo.DEAD_END) {
164                         initsOnReturn = flowInfo.copy().unconditionalInits();
165                 } else {
166                         initsOnReturn = initsOnReturn.mergedWith(flowInfo.unconditionalInits());
167                 }
168         }
169         
170         /*
171          * Compute a merged list of unhandled exception types (keeping only the most generic ones).
172          * This is necessary to add synthetic thrown exceptions for anonymous type constructors (JLS 8.6).
173          */
174         public void mergeUnhandledException(TypeBinding newException){
175                 
176                 if (this.extendedExceptions == null){
177                         this.extendedExceptions = new ArrayList(5);
178                         for (int i = 0; i < this.handledExceptions.length; i++){
179                                 this.extendedExceptions.add(this.handledExceptions[i]);
180                         }
181                 }
182                 
183                 boolean isRedundant = false;
184                 
185                 for(int i = this.extendedExceptions.size()-1; i >= 0; i--){
186                         switch(Scope.compareTypes(newException, (TypeBinding)this.extendedExceptions.get(i))){
187                                 case MoreGeneric :
188                                         this.extendedExceptions.remove(i);
189                                         break;
190                                 case EqualOrMoreSpecific :
191                                         isRedundant = true;
192                                         break;
193                                 case NotRelated :
194                                         break;
195                         }
196                 }
197                 if (!isRedundant){
198                         this.extendedExceptions.add(newException);
199                 }
200         }
201 }