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