Prepared better HEREDOC support; see comment for bug #1319276
[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         public Statement[] statements;
25         public BlockScope scope;
26         public int explicitDeclarations;
27         public Label breakLabel;
28         public CaseStatement[] 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 = expression.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 net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
96          * @param codeStream net.sourceforge.phpdt.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 StringBuffer printStatement(int indent, StringBuffer output) {
211
212                 printIndent(indent, output).append("switch ("); //$NON-NLS-1$
213                 expression.printExpression(0, output).append(") {"); //$NON-NLS-1$
214                 if (statements != null) {
215                         for (int i = 0; i < statements.length; i++) {
216                                 output.append('\n');
217                                 if (statements[i] instanceof CaseStatement) {
218                                         statements[i].printStatement(indent, output);
219                                 } else {
220                                         statements[i].printStatement(indent+2, output);
221                                 }
222                         }
223                 }
224                 output.append("\n"); //$NON-NLS-1$
225                 return printIndent(indent, output).append('}');
226         }
227         public void resolve(BlockScope upperScope) {
228
229                 TypeBinding testType = expression.resolveType(upperScope);
230                 if (testType == null)
231                         return;
232                 expression.implicitWidening(testType, testType);
233                 if (!(expression.isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
234                         if (!testType.isCompatibleWith(IntBinding)) {
235                                 upperScope.problemReporter().incorrectSwitchType(expression, testType);
236                                 return;
237                         }
238                 }
239                 if (statements != null) {
240                         scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
241                         int length;
242                         // collection of cases is too big but we will only iterate until caseCount
243                         cases = new CaseStatement[length = statements.length];
244                         int[] casesValues = new int[length];
245                         int counter = 0;
246                         for (int i = 0; i < length; i++) {
247                                 Constant cst;
248                                 if ((cst = statements[i].resolveCase(scope, testType, this)) != null) {
249                                         //----check for duplicate case statement------------
250                                         if (cst != NotAConstant) {
251                                                 int key = cst.intValue();
252                                                 for (int j = 0; j < counter; j++) {
253                                                         if (casesValues[j] == key) {
254                                                                 scope.problemReporter().duplicateCase((CaseStatement) statements[i], cst); //TODO: (philippe) could improve diagnosis to indicate colliding case
255                                                         }
256                                                 }
257                                                 casesValues[counter++] = key;
258                                         }
259                                 }
260                         }
261                 }
262         }
263         public String toString(int tab) {
264
265                 String inFront, s = tabString(tab);
266                 inFront = s;
267                 s = s + "switch (" + expression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
268                 if (statements == null) {
269                         s = s + "{}"; //$NON-NLS-1$
270                         return s;
271                 } else
272                         s = s + "{"; //$NON-NLS-1$
273                         s = s
274                                         + (explicitDeclarations != 0
275                                                 ? "// ---scope needed for " //$NON-NLS-1$
276                                                         + String.valueOf(explicitDeclarations)
277                                                         + " locals------------ \n"//$NON-NLS-1$
278                                                 : "// ---NO scope needed------ \n"); //$NON-NLS-1$
279
280                 int i = 0;
281                 String tabulation = "  "; //$NON-NLS-1$
282                 try {
283                         while (true) {
284                                 //use instanceof in order not to polluate classes with behavior only needed for printing purpose.
285                                 if (statements[i] instanceof Expression)
286                                         s = s + "\n" + inFront + tabulation; //$NON-NLS-1$
287                                 if (statements[i] instanceof BreakStatement)
288                                         s = s + statements[i].toString(0);
289                                 else
290                                         s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
291                                 //============= 
292                                 if ((statements[i] instanceof CaseStatement)
293                                         || (statements[i] instanceof DefaultCase)) {
294                                         i++;
295                                         while (!((statements[i] instanceof CaseStatement)
296                                                 || (statements[i] instanceof DefaultCase))) {
297                                                 if ((statements[i] instanceof Expression) || (statements[i] instanceof BreakStatement))
298                                                         s = s + statements[i].toString(0) + " ; "; //$NON-NLS-1$
299                                                 else
300                                                         s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
301                                                 i++;
302                                         }
303                                 } else {
304                                         s = s + " ;"; //$NON-NLS-1$
305                                         i++;
306                                 }
307                         }
308                 } catch (IndexOutOfBoundsException e) {
309                 };
310                 s = s + "}"; //$NON-NLS-1$
311                 return s;
312         }
313
314         public void traverse(
315                 ASTVisitor visitor,
316                 BlockScope blockScope) {
317
318                 if (visitor.visit(this, blockScope)) {
319                         expression.traverse(visitor, scope);
320                         if (statements != null) {
321                                 int statementsLength = statements.length;
322                                 for (int i = 0; i < statementsLength; i++)
323                                         statements[i].traverse(visitor, scope);
324                         }
325                 }
326                 visitor.endVisit(this, blockScope);
327         }
328         
329         /**
330          * Dispatch the call on its last statement.
331          */
332         public void branchChainTo(Label label) {
333                 
334                 // in order to improve debug attributes for stepping (11431)
335                 // we want to inline the jumps to #breakLabel which already got
336                 // generated (if any), and have them directly branch to a better
337                 // location (the argument label).
338                 // we know at this point that the breakLabel already got placed
339                 if (this.breakLabel.hasForwardReferences()) {
340                         label.appendForwardReferencesFrom(this.breakLabel);
341                 }
342         }
343 }