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
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
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.*;
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;
26 public DefaultCase defaultCase;
27 public int caseCount = 0;
29 // for local variables table attributes
30 int preSwitchInitStateIndex = -1;
31 int mergedInitStateIndex = -1;
33 * SwitchStatement constructor comment.
35 public SwitchStatement() {
38 public FlowInfo analyseCode(
39 BlockScope currentScope,
40 FlowContext flowContext,
42 flowInfo = testExpression.analyseCode(currentScope, flowContext, flowInfo);
43 SwitchFlowContext switchContext =
44 new SwitchFlowContext(flowContext, this, (breakLabel = new Label()));
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);
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
59 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
61 if (statement == defaultCase) {
62 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
65 if (!caseInits.complainIfUnreachable(statement, scope)) {
66 caseInits = statement.analyseCode(scope, switchContext, caseInits);
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);
81 // merge all branches inits
82 FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
83 mergedInitStateIndex =
84 currentScope.methodScope().recordInitializationStates(mergedInfo);
88 * Switch code generation
90 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
91 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
93 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
94 int[] sortedIndexes = new int[caseCount];
96 if ((bits & IsReachableMASK) == 0) {
99 int pc = codeStream.position;
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));
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;
118 (localKeysCopy = new int[caseCount]),
121 CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes);
122 CaseLabel defaultLabel = new CaseLabel(codeStream);
123 if (defaultCase != null) {
124 defaultCase.targetLabel = defaultLabel;
126 // generate expression testes
127 testExpression.generateCode(currentScope, codeStream, needSwitch);
129 // generate the appropriate switch table
131 int max = localKeysCopy[caseCount - 1];
132 int min = localKeysCopy[0];
133 if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
134 codeStream.tableswitch(
142 codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
144 codeStream.updateLastRecordedEndPC(codeStream.position);
146 // generate the switch block statements
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(
156 preSwitchInitStateIndex);
160 if (statement == defaultCase) { // statements[i] is a case or a default case
161 if (preSwitchInitStateIndex != -1) {
162 codeStream.removeNotDefinitelyAssignedVariables(
164 preSwitchInitStateIndex);
168 statement.generateCode(scope, codeStream);
171 // place the trailing labels (for break and default case)
173 if (defaultCase == null) {
174 defaultLabel.place();
176 // May loose some local variable initializations : affecting the local variable attributes
177 if (mergedInitStateIndex != -1) {
178 codeStream.removeNotDefinitelyAssignedVariables(
180 mergedInitStateIndex);
181 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
183 if (scope != currentScope) {
184 codeStream.exitUserScope(scope);
186 codeStream.recordPositionsFrom(pc, this.sourceStart);
190 public void resetStateForCodeGeneration() {
192 this.breakLabel.resetStateForCodeGeneration();
195 public void resolve(BlockScope upperScope) {
197 TypeBinding testType = testExpression.resolveType(upperScope);
198 if (testType == null)
200 testExpression.implicitWidening(testType, testType);
202 .isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
203 if (!upperScope.areTypesCompatible(testType, IntBinding)) {
204 upperScope.problemReporter().incorrectSwitchType(testExpression, testType);
208 if (statements != null) {
209 scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
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];
215 for (int i = 0; i < length; i++) {
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);
226 casesValues[counter++] = key;
232 public String toString(int tab) {
234 String inFront, s = tabString(tab);
236 s = s + "switch (" + testExpression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
237 if (statements == null) {
238 s = s + "{}"; //$NON-NLS-1$
241 s = s + "{"; //$NON-NLS-1$
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$
250 String tabulation = " "; //$NON-NLS-1$
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);
259 s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
261 if ((statements[i] instanceof Case)
262 || (statements[i] instanceof DefaultCase)) {
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$
269 s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
273 s = s + " ;"; //$NON-NLS-1$
277 } catch (IndexOutOfBoundsException e) {
279 s = s + "}"; //$NON-NLS-1$
283 public void traverse(
284 IAbstractSyntaxTreeVisitor visitor,
285 BlockScope blockScope) {
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);
295 visitor.endVisit(this, blockScope);
299 * Dispatch the call on its last statement.
301 public void branchChainTo(Label label) {
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);