18561a44e84cce1a5cf4e3adc93b624779f1619e
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / SwitchStatement.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.CaseLabel;
15 import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream;
16 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
17 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
18 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
19 import net.sourceforge.phpdt.internal.compiler.flow.SwitchFlowContext;
20 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
21 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
22 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
23
24 public class SwitchStatement extends Statement {
25         public Expression testExpression;
26         public Statement[] statements;
27         public BlockScope scope;
28         public int explicitDeclarations;
29         public Label breakLabel;
30         public Case[] cases;
31         public DefaultCase defaultCase;
32         public int caseCount = 0;
33
34         // for local variables table attributes
35         int preSwitchInitStateIndex = -1;
36         int mergedInitStateIndex = -1;
37         /**
38          * SwitchStatement constructor comment.
39          */
40         public SwitchStatement() {
41                 super();
42         }
43         public FlowInfo analyseCode(
44                 BlockScope currentScope,
45                 FlowContext flowContext,
46                 FlowInfo flowInfo) {
47                 flowInfo = testExpression.analyseCode(currentScope, flowContext, flowInfo);
48                 SwitchFlowContext switchContext =
49                         new SwitchFlowContext(flowContext, this, (breakLabel = new Label()));
50
51                 // analyse the block by considering specially the case/default statements (need to bind them 
52                 // to the entry point)
53                 FlowInfo caseInits = FlowInfo.DeadEnd;
54                 // in case of statements before the first case
55                 preSwitchInitStateIndex =
56                         currentScope.methodScope().recordInitializationStates(flowInfo);
57                 int caseIndex = 0;
58                 if (statements != null) {
59                         for (int i = 0, max = statements.length; i < max; i++) {
60                                 Statement statement = statements[i];
61                                 if ((caseIndex < caseCount)
62                                         && (statement == cases[caseIndex])) { // statements[i] is a case or a default case
63                                         caseIndex++;
64                                         caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
65                                 } else {
66                                         if (statement == defaultCase) {
67                                                 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
68                                         }
69                                 }
70                                 if (!caseInits.complainIfUnreachable(statement, scope)) {
71                                         caseInits = statement.analyseCode(scope, switchContext, caseInits);
72                                 }
73                         }
74                 }
75
76                 // if no default case, then record it may jump over the block directly to the end
77                 if (defaultCase == null) {
78                         // only retain the potential initializations
79                         flowInfo.addPotentialInitializationsFrom(
80                                 caseInits.mergedWith(switchContext.initsOnBreak));
81                         mergedInitStateIndex =
82                                 currentScope.methodScope().recordInitializationStates(flowInfo);
83                         return flowInfo;
84                 }
85
86                 // merge all branches inits
87                 FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
88                 mergedInitStateIndex =
89                         currentScope.methodScope().recordInitializationStates(mergedInfo);
90                 return mergedInfo;
91         }
92         /**
93          * Switch code generation
94          *
95          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
96          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
97          */
98         public void generateCode(BlockScope currentScope, CodeStream codeStream) {
99                 int[] sortedIndexes = new int[caseCount];
100                 int[] localKeysCopy;
101                 if ((bits & IsReachableMASK) == 0) {
102                         return;
103                 }
104                 int pc = codeStream.position;
105
106                 // prepare the labels and constants
107                 breakLabel.codeStream = codeStream;
108                 CaseLabel[] caseLabels = new CaseLabel[caseCount];
109                 int[] constants = new int[caseCount];
110                 boolean needSwitch = caseCount != 0;
111                 for (int i = 0; i < caseCount; i++) {
112                         constants[i] = cases[i].constantExpression.constant.intValue();
113                         cases[i].targetLabel = (caseLabels[i] = new CaseLabel(codeStream));
114                 }
115
116                 // we sort the keys to be able to generate the code for tableswitch or lookupswitch
117                 for (int i = 0; i < caseCount; i++) {
118                         sortedIndexes[i] = i;
119                 }
120                 System.arraycopy(
121                         constants,
122                         0,
123                         (localKeysCopy = new int[caseCount]),
124                         0,
125                         caseCount);
126                 CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes);
127                 CaseLabel defaultLabel = new CaseLabel(codeStream);
128                 if (defaultCase != null) {
129                         defaultCase.targetLabel = defaultLabel;
130                 }
131                 // generate expression testes
132                 testExpression.generateCode(currentScope, codeStream, needSwitch);
133
134                 // generate the appropriate switch table
135                 if (needSwitch) {
136                         int max = localKeysCopy[caseCount - 1];
137                         int min = localKeysCopy[0];
138                         if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
139                                 codeStream.tableswitch(
140                                         defaultLabel,
141                                         min,
142                                         max,
143                                         constants,
144                                         sortedIndexes,
145                                         caseLabels);
146                         } else {
147                                 codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
148                         }
149                         codeStream.updateLastRecordedEndPC(codeStream.position);
150                 }
151                 // generate the switch block statements
152                 int caseIndex = 0;
153                 if (statements != null) {
154                         for (int i = 0, maxCases = statements.length; i < maxCases; i++) {
155                                 Statement statement = statements[i];
156                                 if ((caseIndex < caseCount)
157                                         && (statement == cases[caseIndex])) { // statements[i] is a case
158                                         if (preSwitchInitStateIndex != -1) {
159                                                 codeStream.removeNotDefinitelyAssignedVariables(
160                                                         currentScope,
161                                                         preSwitchInitStateIndex);
162                                                 caseIndex++;
163                                         }
164                                 } else {
165                                         if (statement == defaultCase) { // statements[i] is a case or a default case
166                                                 if (preSwitchInitStateIndex != -1) {
167                                                         codeStream.removeNotDefinitelyAssignedVariables(
168                                                                 currentScope,
169                                                                 preSwitchInitStateIndex);
170                                                 }
171                                         }
172                                 }
173                                 statement.generateCode(scope, codeStream);
174                         }
175                 }
176                 // place the trailing labels (for break and default case)
177                 breakLabel.place();
178                 if (defaultCase == null) {
179                         defaultLabel.place();
180                 }
181                 // May loose some local variable initializations : affecting the local variable attributes
182                 if (mergedInitStateIndex != -1) {
183                         codeStream.removeNotDefinitelyAssignedVariables(
184                                 currentScope,
185                                 mergedInitStateIndex);
186                         codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
187                 }
188                 if (scope != currentScope) {
189                         codeStream.exitUserScope(scope);
190                 }
191                 codeStream.recordPositionsFrom(pc, this.sourceStart);
192         }
193
194
195         public void resetStateForCodeGeneration() {
196
197                 this.breakLabel.resetStateForCodeGeneration();
198         }
199
200         public void resolve(BlockScope upperScope) {
201
202                 TypeBinding testType = testExpression.resolveType(upperScope);
203                 if (testType == null)
204                         return;
205                 testExpression.implicitWidening(testType, testType);
206                 if (!(testExpression
207                         .isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
208                         if (!BlockScope.areTypesCompatible(testType, IntBinding)) {
209                                 upperScope.problemReporter().incorrectSwitchType(testExpression, testType);
210                                 return;
211                         }
212                 }
213                 if (statements != null) {
214                         scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
215                         int length;
216                         // collection of cases is too big but we will only iterate until caseCount
217                         cases = new Case[length = statements.length];
218                         int[] casesValues = new int[length];
219                         int counter = 0;
220                         for (int i = 0; i < length; i++) {
221                                 Constant cst;
222                                 if ((cst = statements[i].resolveCase(scope, testType, this)) != null) {
223                                         //----check for duplicate case statement------------
224                                         if (cst != NotAConstant) {
225                                                 int key = cst.intValue();
226                                                 for (int j = 0; j < counter; j++) {
227                                                         if (casesValues[j] == key) {
228                                                                 scope.problemReporter().duplicateCase((Case) statements[i], cst);
229                                                         }
230                                                 }
231                                                 casesValues[counter++] = key;
232                                         }
233                                 }
234                         }
235                 }
236         }
237         public String toString(int tab) {
238
239                 String inFront, s = tabString(tab);
240                 inFront = s;
241                 s = s + "switch (" + testExpression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
242                 if (statements == null) {
243                         s = s + "{}"; //$NON-NLS-1$
244                         return s;
245                 } else
246                         s = s + "{"; //$NON-NLS-1$
247                         s = s
248                                         + (explicitDeclarations != 0
249                                                 ? "// ---scope needed for " //$NON-NLS-1$
250                                                         + String.valueOf(explicitDeclarations)
251                                                         + " locals------------ \n"//$NON-NLS-1$
252                                                 : "// ---NO scope needed------ \n"); //$NON-NLS-1$
253
254                 int i = 0;
255                 String tabulation = "  "; //$NON-NLS-1$
256                 try {
257                         while (true) {
258                                 //use instanceof in order not to polluate classes with behavior only needed for printing purpose.
259                                 if (statements[i] instanceof Expression)
260                                         s = s + "\n" + inFront + tabulation; //$NON-NLS-1$
261                                 if (statements[i] instanceof Break)
262                                         s = s + statements[i].toString(0);
263                                 else
264                                         s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
265                                 //============= 
266                                 if ((statements[i] instanceof Case)
267                                         || (statements[i] instanceof DefaultCase)) {
268                                         i++;
269                                         while (!((statements[i] instanceof Case)
270                                                 || (statements[i] instanceof DefaultCase))) {
271                                                 if ((statements[i] instanceof Expression) || (statements[i] instanceof Break))
272                                                         s = s + statements[i].toString(0) + " ; "; //$NON-NLS-1$
273                                                 else
274                                                         s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
275                                                 i++;
276                                         }
277                                 } else {
278                                         s = s + " ;"; //$NON-NLS-1$
279                                         i++;
280                                 }
281                         }
282                 } catch (IndexOutOfBoundsException e) {
283                 };
284                 s = s + "}"; //$NON-NLS-1$
285                 return s;
286         }
287
288         public void traverse(
289                 IAbstractSyntaxTreeVisitor visitor,
290                 BlockScope blockScope) {
291
292                 if (visitor.visit(this, blockScope)) {
293                         testExpression.traverse(visitor, scope);
294                         if (statements != null) {
295                                 int statementsLength = statements.length;
296                                 for (int i = 0; i < statementsLength; i++)
297                                         statements[i].traverse(visitor, scope);
298                         }
299                 }
300                 visitor.endVisit(this, blockScope);
301         }
302         
303         /**
304          * Dispatch the call on its last statement.
305          */
306         public void branchChainTo(Label label) {
307                 
308                 // in order to improve debug attributes for stepping (11431)
309                 // we want to inline the jumps to #breakLabel which already got
310                 // generated (if any), and have them directly branch to a better
311                 // location (the argument label).
312                 // we know at this point that the breakLabel already got placed
313                 if (this.breakLabel.hasForwardReferences()) {
314                         label.appendForwardReferencesFrom(this.breakLabel);
315                 }
316         }
317 }