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