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