code-template needs new context type
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / 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.phpeclipse.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
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 testExpression;
24         public Statement[] statements;
25         public BlockScope scope;
26         public int explicitDeclarations;
27         public Label breakLabel;
28         public Case[] cases;
29         public DefaultCase defaultCase;
30         public int caseCount = 0;
31
32         // for local variables table attributes
33         int preSwitchInitStateIndex = -1;
34         int mergedInitStateIndex = -1;
35         /**
36          * SwitchStatement constructor comment.
37          */
38         public SwitchStatement() {
39                 super();
40         }
41         public FlowInfo analyseCode(
42                 BlockScope currentScope,
43                 FlowContext flowContext,
44                 FlowInfo flowInfo) {
45                 flowInfo = testExpression.analyseCode(currentScope, flowContext, flowInfo);
46                 SwitchFlowContext switchContext =
47                         new SwitchFlowContext(flowContext, this, (breakLabel = new Label()));
48
49                 // analyse the block by considering specially the case/default statements (need to bind them 
50                 // to the entry point)
51                 FlowInfo caseInits = FlowInfo.DEAD_END;
52                 // in case of statements before the first case
53                 preSwitchInitStateIndex =
54                         currentScope.methodScope().recordInitializationStates(flowInfo);
55                 int caseIndex = 0;
56                 if (statements != null) {
57                         boolean didAlreadyComplain = false;
58                         for (int i = 0, max = statements.length; i < max; i++) {
59                                 Statement statement = statements[i];
60                                 if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement is a case
61                                         caseIndex++;
62                                         caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
63                                         didAlreadyComplain = false; // reset complaint
64                                 } else if (statement == defaultCase) { // statement is the default case
65                                         caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
66                                         didAlreadyComplain = false; // reset complaint
67                                 }
68                                 if (!caseInits.complainIfUnreachable(statement, scope, didAlreadyComplain)) {
69                                         caseInits = statement.analyseCode(scope, switchContext, caseInits);
70                                 } else {
71                                         didAlreadyComplain = true;
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/lookup bytecode
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 //                              
140 //                              // work-around 1.3 VM bug, if max>0x7FFF0000, must use lookup bytecode
141 //                              // see http://dev.eclipse.org/bugs/show_bug.cgi?id=21557
142 //                              if (max > 0x7FFF0000 && currentScope.environment().options.complianceLevel < CompilerOptions.JDK1_4) {
143 //                                      codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
144 //
145 //                              } else {
146 //                                      codeStream.tableswitch(
147 //                                              defaultLabel,
148 //                                              min,
149 //                                              max,
150 //                                              constants,
151 //                                              sortedIndexes,
152 //                                              caseLabels);
153 //                              }
154 //                      } else {
155 //                              codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
156 //                      }
157 //                      codeStream.updateLastRecordedEndPC(codeStream.position);
158 //              }
159 //              
160 //              // generate the switch block statements
161 //              int caseIndex = 0;
162 //              if (statements != null) {
163 //                      for (int i = 0, maxCases = statements.length; i < maxCases; i++) {
164 //                              Statement statement = statements[i];
165 //                              if ((caseIndex < caseCount)
166 //                                      && (statement == cases[caseIndex])) { // statements[i] is a case
167 //                                      if (preSwitchInitStateIndex != -1) {
168 //                                              codeStream.removeNotDefinitelyAssignedVariables(
169 //                                                      currentScope,
170 //                                                      preSwitchInitStateIndex);
171 //                                              caseIndex++;
172 //                                      }
173 //                              } else {
174 //                                      if (statement == defaultCase) { // statements[i] is a case or a default case
175 //                                              if (preSwitchInitStateIndex != -1) {
176 //                                                      codeStream.removeNotDefinitelyAssignedVariables(
177 //                                                              currentScope,
178 //                                                              preSwitchInitStateIndex);
179 //                                              }
180 //                                      }
181 //                              }
182 //                              statement.generateCode(scope, codeStream);
183 //                      }
184 //              }
185 //              // place the trailing labels (for break and default case)
186 //              breakLabel.place();
187 //              if (defaultCase == null) {
188 //                      defaultLabel.place();
189 //              }
190 //              // May loose some local variable initializations : affecting the local variable attributes
191 //              if (mergedInitStateIndex != -1) {
192 //                      codeStream.removeNotDefinitelyAssignedVariables(
193 //                              currentScope,
194 //                              mergedInitStateIndex);
195 //                      codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
196 //              }
197 //              if (scope != currentScope) {
198 //                      codeStream.exitUserScope(scope);
199 //              }
200 //              codeStream.recordPositionsFrom(pc, this.sourceStart);
201 //      }
202
203
204         public void resetStateForCodeGeneration() {
205                 if (this.breakLabel != null) {
206                         this.breakLabel.resetStateForCodeGeneration();
207                 }
208         }
209
210         public void resolve(BlockScope upperScope) {
211
212                 TypeBinding testType = testExpression.resolveType(upperScope);
213                 if (testType == null)
214                         return;
215                 testExpression.implicitWidening(testType, testType);
216                 if (!(testExpression.isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
217                         if (!testType.isCompatibleWith(IntBinding)) {
218                                 upperScope.problemReporter().incorrectSwitchType(testExpression, testType);
219                                 return;
220                         }
221                 }
222                 if (statements != null) {
223                         scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
224                         int length;
225                         // collection of cases is too big but we will only iterate until caseCount
226                         cases = new Case[length = statements.length];
227                         int[] casesValues = new int[length];
228                         int counter = 0;
229                         for (int i = 0; i < length; i++) {
230                                 Constant cst;
231                                 if ((cst = statements[i].resolveCase(scope, testType, this)) != null) {
232                                         //----check for duplicate case statement------------
233                                         if (cst != NotAConstant) {
234                                                 int key = cst.intValue();
235                                                 for (int j = 0; j < counter; j++) {
236                                                         if (casesValues[j] == key) {
237                                                                 scope.problemReporter().duplicateCase((Case) statements[i], cst); //TODO: (philippe) could improve diagnosis to indicate colliding case
238                                                         }
239                                                 }
240                                                 casesValues[counter++] = key;
241                                         }
242                                 }
243                         }
244                 }
245         }
246         public String toString(int tab) {
247
248                 String inFront, s = tabString(tab);
249                 inFront = s;
250                 s = s + "switch (" + testExpression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
251                 if (statements == null) {
252                         s = s + "{}"; //$NON-NLS-1$
253                         return s;
254                 } else
255                         s = s + "{"; //$NON-NLS-1$
256                         s = s
257                                         + (explicitDeclarations != 0
258                                                 ? "// ---scope needed for " //$NON-NLS-1$
259                                                         + String.valueOf(explicitDeclarations)
260                                                         + " locals------------ \n"//$NON-NLS-1$
261                                                 : "// ---NO scope needed------ \n"); //$NON-NLS-1$
262
263                 int i = 0;
264                 String tabulation = "  "; //$NON-NLS-1$
265                 try {
266                         while (true) {
267                                 //use instanceof in order not to polluate classes with behavior only needed for printing purpose.
268                                 if (statements[i] instanceof Expression)
269                                         s = s + "\n" + inFront + tabulation; //$NON-NLS-1$
270                                 if (statements[i] instanceof Break)
271                                         s = s + statements[i].toString(0);
272                                 else
273                                         s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
274                                 //============= 
275                                 if ((statements[i] instanceof Case)
276                                         || (statements[i] instanceof DefaultCase)) {
277                                         i++;
278                                         while (!((statements[i] instanceof Case)
279                                                 || (statements[i] instanceof DefaultCase))) {
280                                                 if ((statements[i] instanceof Expression) || (statements[i] instanceof Break))
281                                                         s = s + statements[i].toString(0) + " ; "; //$NON-NLS-1$
282                                                 else
283                                                         s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
284                                                 i++;
285                                         }
286                                 } else {
287                                         s = s + " ;"; //$NON-NLS-1$
288                                         i++;
289                                 }
290                         }
291                 } catch (IndexOutOfBoundsException e) {
292                 };
293                 s = s + "}"; //$NON-NLS-1$
294                 return s;
295         }
296
297         public void traverse(
298                 IAbstractSyntaxTreeVisitor visitor,
299                 BlockScope blockScope) {
300
301                 if (visitor.visit(this, blockScope)) {
302                         testExpression.traverse(visitor, scope);
303                         if (statements != null) {
304                                 int statementsLength = statements.length;
305                                 for (int i = 0; i < statementsLength; i++)
306                                         statements[i].traverse(visitor, scope);
307                         }
308                 }
309                 visitor.endVisit(this, blockScope);
310         }
311         
312         /**
313          * Dispatch the call on its last statement.
314          */
315         public void branchChainTo(Label label) {
316                 
317                 // in order to improve debug attributes for stepping (11431)
318                 // we want to inline the jumps to #breakLabel which already got
319                 // generated (if any), and have them directly branch to a better
320                 // location (the argument label).
321                 // we know at this point that the breakLabel already got placed
322                 if (this.breakLabel.hasForwardReferences()) {
323                         label.appendForwardReferencesFrom(this.breakLabel);
324                 }
325         }
326 }