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