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.codegen.CaseLabel;
15 import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream;
16 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
17 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
18 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
19 import net.sourceforge.phpdt.internal.compiler.flow.SwitchFlowContext;
20 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
21 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
22 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
24 public class SwitchStatement extends Statement {
25 public Expression testExpression;
26 public Statement[] statements;
27 public BlockScope scope;
28 public int explicitDeclarations;
29 public Label breakLabel;
31 public DefaultCase defaultCase;
32 public int caseCount = 0;
34 // for local variables table attributes
35 int preSwitchInitStateIndex = -1;
36 int mergedInitStateIndex = -1;
38 * SwitchStatement constructor comment.
40 public SwitchStatement() {
43 public FlowInfo analyseCode(
44 BlockScope currentScope,
45 FlowContext flowContext,
47 flowInfo = testExpression.analyseCode(currentScope, flowContext, flowInfo);
48 SwitchFlowContext switchContext =
49 new SwitchFlowContext(flowContext, this, (breakLabel = new Label()));
51 // analyse the block by considering specially the case/default statements (need to bind them
52 // to the entry point)
53 FlowInfo caseInits = FlowInfo.DeadEnd;
54 // in case of statements before the first case
55 preSwitchInitStateIndex =
56 currentScope.methodScope().recordInitializationStates(flowInfo);
58 if (statements != null) {
59 for (int i = 0, max = statements.length; i < max; i++) {
60 Statement statement = statements[i];
61 if ((caseIndex < caseCount)
62 && (statement == cases[caseIndex])) { // statements[i] is a case or a default case
64 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
66 if (statement == defaultCase) {
67 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
70 if (!caseInits.complainIfUnreachable(statement, scope)) {
71 caseInits = statement.analyseCode(scope, switchContext, caseInits);
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 org.eclipse.jdt.internal.compiler.lookup.BlockScope
96 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
98 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
99 int[] sortedIndexes = new int[caseCount];
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
136 int max = localKeysCopy[caseCount - 1];
137 int min = localKeysCopy[0];
138 if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
139 codeStream.tableswitch(
147 codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
149 codeStream.updateLastRecordedEndPC(codeStream.position);
151 // generate the switch block statements
153 if (statements != null) {
154 for (int i = 0, maxCases = statements.length; i < maxCases; i++) {
155 Statement statement = statements[i];
156 if ((caseIndex < caseCount)
157 && (statement == cases[caseIndex])) { // statements[i] is a case
158 if (preSwitchInitStateIndex != -1) {
159 codeStream.removeNotDefinitelyAssignedVariables(
161 preSwitchInitStateIndex);
165 if (statement == defaultCase) { // statements[i] is a case or a default case
166 if (preSwitchInitStateIndex != -1) {
167 codeStream.removeNotDefinitelyAssignedVariables(
169 preSwitchInitStateIndex);
173 statement.generateCode(scope, codeStream);
176 // place the trailing labels (for break and default case)
178 if (defaultCase == null) {
179 defaultLabel.place();
181 // May loose some local variable initializations : affecting the local variable attributes
182 if (mergedInitStateIndex != -1) {
183 codeStream.removeNotDefinitelyAssignedVariables(
185 mergedInitStateIndex);
186 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
188 if (scope != currentScope) {
189 codeStream.exitUserScope(scope);
191 codeStream.recordPositionsFrom(pc, this.sourceStart);
195 public void resetStateForCodeGeneration() {
197 this.breakLabel.resetStateForCodeGeneration();
200 public void resolve(BlockScope upperScope) {
202 TypeBinding testType = testExpression.resolveType(upperScope);
203 if (testType == null)
205 testExpression.implicitWidening(testType, testType);
207 .isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
208 if (!BlockScope.areTypesCompatible(testType, IntBinding)) {
209 upperScope.problemReporter().incorrectSwitchType(testExpression, testType);
213 if (statements != null) {
214 scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
216 // collection of cases is too big but we will only iterate until caseCount
217 cases = new Case[length = statements.length];
218 int[] casesValues = new int[length];
220 for (int i = 0; i < length; i++) {
222 if ((cst = statements[i].resolveCase(scope, testType, this)) != null) {
223 //----check for duplicate case statement------------
224 if (cst != NotAConstant) {
225 int key = cst.intValue();
226 for (int j = 0; j < counter; j++) {
227 if (casesValues[j] == key) {
228 scope.problemReporter().duplicateCase((Case) statements[i], cst);
231 casesValues[counter++] = key;
237 public String toString(int tab) {
239 String inFront, s = tabString(tab);
241 s = s + "switch (" + testExpression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
242 if (statements == null) {
243 s = s + "{}"; //$NON-NLS-1$
246 s = s + "{"; //$NON-NLS-1$
248 + (explicitDeclarations != 0
249 ? "// ---scope needed for " //$NON-NLS-1$
250 + String.valueOf(explicitDeclarations)
251 + " locals------------ \n"//$NON-NLS-1$
252 : "// ---NO scope needed------ \n"); //$NON-NLS-1$
255 String tabulation = " "; //$NON-NLS-1$
258 //use instanceof in order not to polluate classes with behavior only needed for printing purpose.
259 if (statements[i] instanceof Expression)
260 s = s + "\n" + inFront + tabulation; //$NON-NLS-1$
261 if (statements[i] instanceof Break)
262 s = s + statements[i].toString(0);
264 s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
266 if ((statements[i] instanceof Case)
267 || (statements[i] instanceof DefaultCase)) {
269 while (!((statements[i] instanceof Case)
270 || (statements[i] instanceof DefaultCase))) {
271 if ((statements[i] instanceof Expression) || (statements[i] instanceof Break))
272 s = s + statements[i].toString(0) + " ; "; //$NON-NLS-1$
274 s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
278 s = s + " ;"; //$NON-NLS-1$
282 } catch (IndexOutOfBoundsException e) {
284 s = s + "}"; //$NON-NLS-1$
288 public void traverse(
289 IAbstractSyntaxTreeVisitor visitor,
290 BlockScope blockScope) {
292 if (visitor.visit(this, blockScope)) {
293 testExpression.traverse(visitor, scope);
294 if (statements != null) {
295 int statementsLength = statements.length;
296 for (int i = 0; i < statementsLength; i++)
297 statements[i].traverse(visitor, scope);
300 visitor.endVisit(this, blockScope);
304 * Dispatch the call on its last statement.
306 public void branchChainTo(Label label) {
308 // in order to improve debug attributes for stepping (11431)
309 // we want to inline the jumps to #breakLabel which already got
310 // generated (if any), and have them directly branch to a better
311 // location (the argument label).
312 // we know at this point that the breakLabel already got placed
313 if (this.breakLabel.hasForwardReferences()) {
314 label.appendForwardReferencesFrom(this.breakLabel);