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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
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;
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;
32 // for local variables table attributes
33 int preSwitchInitStateIndex = -1;
34 int mergedInitStateIndex = -1;
36 * SwitchStatement constructor comment.
38 public SwitchStatement() {
41 public FlowInfo analyseCode(
42 BlockScope currentScope,
43 FlowContext flowContext,
45 flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo);
46 SwitchFlowContext switchContext =
47 new SwitchFlowContext(flowContext, this, (breakLabel = new Label()));
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);
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
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
68 if (!caseInits.complainIfUnreachable(statement, scope, didAlreadyComplain)) {
69 caseInits = statement.analyseCode(scope, switchContext, caseInits);
71 didAlreadyComplain = true;
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);
86 // merge all branches inits
87 FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
88 mergedInitStateIndex =
89 currentScope.methodScope().recordInitializationStates(mergedInfo);
93 * Switch code generation
95 * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
96 * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
98 // public void generateCode(BlockScope currentScope, CodeStream codeStream) {
99 // int[] sortedIndexes = new int[caseCount];
100 // int[] localKeysCopy;
101 // if ((bits & IsReachableMASK) == 0) {
104 // int pc = codeStream.position;
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));
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;
123 // (localKeysCopy = new int[caseCount]),
126 // CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes);
127 // CaseLabel defaultLabel = new CaseLabel(codeStream);
128 // if (defaultCase != null) {
129 // defaultCase.targetLabel = defaultLabel;
131 // // generate expression testes
132 // testExpression.generateCode(currentScope, codeStream, needSwitch);
134 // // generate the appropriate switch table/lookup bytecode
136 // int max = localKeysCopy[caseCount - 1];
137 // int min = localKeysCopy[0];
138 // if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
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);
146 // codeStream.tableswitch(
155 // codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
157 // codeStream.updateLastRecordedEndPC(codeStream.position);
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(
170 // preSwitchInitStateIndex);
174 // if (statement == defaultCase) { // statements[i] is a case or a default case
175 // if (preSwitchInitStateIndex != -1) {
176 // codeStream.removeNotDefinitelyAssignedVariables(
178 // preSwitchInitStateIndex);
182 // statement.generateCode(scope, codeStream);
185 // // place the trailing labels (for break and default case)
186 // breakLabel.place();
187 // if (defaultCase == null) {
188 // defaultLabel.place();
190 // // May loose some local variable initializations : affecting the local variable attributes
191 // if (mergedInitStateIndex != -1) {
192 // codeStream.removeNotDefinitelyAssignedVariables(
194 // mergedInitStateIndex);
195 // codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
197 // if (scope != currentScope) {
198 // codeStream.exitUserScope(scope);
200 // codeStream.recordPositionsFrom(pc, this.sourceStart);
204 public void resetStateForCodeGeneration() {
205 if (this.breakLabel != null) {
206 this.breakLabel.resetStateForCodeGeneration();
210 public StringBuffer printStatement(int indent, StringBuffer output) {
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++) {
217 if (statements[i] instanceof CaseStatement) {
218 statements[i].printStatement(indent, output);
220 statements[i].printStatement(indent+2, output);
224 output.append("\n"); //$NON-NLS-1$
225 return printIndent(indent, output).append('}');
227 public void resolve(BlockScope upperScope) {
229 TypeBinding testType = expression.resolveType(upperScope);
230 if (testType == null)
232 expression.implicitWidening(testType, testType);
233 if (!(expression.isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
234 if (!testType.isCompatibleWith(IntBinding)) {
235 upperScope.problemReporter().incorrectSwitchType(expression, testType);
239 if (statements != null) {
240 scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
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];
246 for (int i = 0; i < length; i++) {
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
257 casesValues[counter++] = key;
263 public String toString(int tab) {
265 String inFront, s = tabString(tab);
267 s = s + "switch (" + expression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
268 if (statements == null) {
269 s = s + "{}"; //$NON-NLS-1$
272 s = s + "{"; //$NON-NLS-1$
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$
281 String tabulation = " "; //$NON-NLS-1$
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);
290 s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
292 if ((statements[i] instanceof CaseStatement)
293 || (statements[i] instanceof DefaultCase)) {
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$
300 s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
304 s = s + " ;"; //$NON-NLS-1$
308 } catch (IndexOutOfBoundsException e) {
310 s = s + "}"; //$NON-NLS-1$
314 public void traverse(
316 BlockScope blockScope) {
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);
326 visitor.endVisit(this, blockScope);
330 * Dispatch the call on its last statement.
332 public void branchChainTo(Label label) {
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);