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.phpeclipse.internal.compiler.ast;
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;
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;
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 = testExpression.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 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];
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 void resolve(BlockScope upperScope) {
212 TypeBinding testType = testExpression.resolveType(upperScope);
213 if (testType == null)
215 testExpression.implicitWidening(testType, testType);
216 if (!(testExpression.isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
217 if (!testType.isCompatibleWith(IntBinding)) {
218 upperScope.problemReporter().incorrectSwitchType(testExpression, testType);
222 if (statements != null) {
223 scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
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];
229 for (int i = 0; i < length; i++) {
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
240 casesValues[counter++] = key;
246 public String toString(int tab) {
248 String inFront, s = tabString(tab);
250 s = s + "switch (" + testExpression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
251 if (statements == null) {
252 s = s + "{}"; //$NON-NLS-1$
255 s = s + "{"; //$NON-NLS-1$
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$
264 String tabulation = " "; //$NON-NLS-1$
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);
273 s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
275 if ((statements[i] instanceof Case)
276 || (statements[i] instanceof DefaultCase)) {
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$
283 s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
287 s = s + " ;"; //$NON-NLS-1$
291 } catch (IndexOutOfBoundsException e) {
293 s = s + "}"; //$NON-NLS-1$
297 public void traverse(
298 IAbstractSyntaxTreeVisitor visitor,
299 BlockScope blockScope) {
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);
309 visitor.endVisit(this, blockScope);
313 * Dispatch the call on its last statement.
315 public void branchChainTo(Label label) {
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);