1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.ast;
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.impl.*;
15 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
16 import org.eclipse.jdt.internal.compiler.codegen.*;
17 import org.eclipse.jdt.internal.compiler.flow.*;
18 import org.eclipse.jdt.internal.compiler.lookup.*;
20 public class SwitchStatement extends Statement {
22 public Expression expression;
23 public Statement[] statements;
24 public BlockScope scope;
25 public int explicitDeclarations;
26 public Label breakLabel;
27 public CaseStatement[] cases;
28 public CaseStatement defaultCase;
29 public int caseCount = 0;
30 public int blockStart;
32 // for local variables table attributes
33 int preSwitchInitStateIndex = -1;
34 int mergedInitStateIndex = -1;
36 public FlowInfo analyseCode(
37 BlockScope currentScope,
38 FlowContext flowContext,
42 flowInfo = expression.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.DEAD_END;
49 // in case of statements before the first case
50 preSwitchInitStateIndex =
51 currentScope.methodScope().recordInitializationStates(flowInfo);
53 if (statements != null) {
54 boolean didAlreadyComplain = false;
55 for (int i = 0, max = statements.length; i < max; i++) {
56 Statement statement = statements[i];
57 if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement is a case
58 this.scope.switchCase = cases[caseIndex]; // record entering in a switch case block
60 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
61 didAlreadyComplain = false; // reset complaint
62 } else if (statement == defaultCase) { // statement is the default case
63 this.scope.switchCase = defaultCase; // record entering in a switch case block
64 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
65 didAlreadyComplain = false; // reset complaint
67 if (!statement.complainIfUnreachable(caseInits, scope, didAlreadyComplain)) {
68 caseInits = statement.analyseCode(scope, switchContext, caseInits);
70 didAlreadyComplain = true;
75 // if no default case, then record it may jump over the block directly to the end
76 if (defaultCase == null) {
77 // only retain the potential initializations
78 flowInfo.addPotentialInitializationsFrom(
79 caseInits.mergedWith(switchContext.initsOnBreak));
80 mergedInitStateIndex =
81 currentScope.methodScope().recordInitializationStates(flowInfo);
85 // merge all branches inits
86 FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
87 mergedInitStateIndex =
88 currentScope.methodScope().recordInitializationStates(mergedInfo);
91 if (this.scope != null) this.scope.switchCase = null; // no longer inside switch case block
96 * Switch code generation
98 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
99 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
101 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
104 int[] sortedIndexes = new int[caseCount];
106 if ((bits & IsReachableMASK) == 0) {
109 int pc = codeStream.position;
111 // prepare the labels and constants
112 breakLabel.initialize(codeStream);
113 CaseLabel[] caseLabels = new CaseLabel[caseCount];
114 int[] constants = new int[caseCount];
115 boolean needSwitch = caseCount != 0;
116 for (int i = 0; i < caseCount; i++) {
117 constants[i] = cases[i].constantExpression.constant.intValue();
118 cases[i].targetLabel = (caseLabels[i] = new CaseLabel(codeStream));
121 // we sort the keys to be able to generate the code for tableswitch or lookupswitch
122 for (int i = 0; i < caseCount; i++) {
123 sortedIndexes[i] = i;
128 (localKeysCopy = new int[caseCount]),
131 CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes);
132 CaseLabel defaultLabel = new CaseLabel(codeStream);
133 if (defaultCase != null) {
134 defaultCase.targetLabel = defaultLabel;
136 // generate expression testes
137 expression.generateCode(currentScope, codeStream, needSwitch);
139 // generate the appropriate switch table/lookup bytecode
141 int max = localKeysCopy[caseCount - 1];
142 int min = localKeysCopy[0];
143 if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
145 // work-around 1.3 VM bug, if max>0x7FFF0000, must use lookup bytecode
146 // see http://dev.eclipse.org/bugs/show_bug.cgi?id=21557
147 if (max > 0x7FFF0000 && currentScope.environment().options.complianceLevel < ClassFileConstants.JDK1_4) {
148 codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
151 codeStream.tableswitch(
160 codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
162 codeStream.updateLastRecordedEndPC(codeStream.position);
165 // generate the switch block statements
167 if (statements != null) {
168 for (int i = 0, maxCases = statements.length; i < maxCases; i++) {
169 Statement statement = statements[i];
170 if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statements[i] is a case
171 this.scope.switchCase = cases[caseIndex]; // record entering in a switch case block
172 if (preSwitchInitStateIndex != -1) {
173 codeStream.removeNotDefinitelyAssignedVariables(
175 preSwitchInitStateIndex);
179 if (statement == defaultCase) { // statements[i] is a case or a default case
180 this.scope.switchCase = defaultCase; // record entering in a switch case block
181 if (preSwitchInitStateIndex != -1) {
182 codeStream.removeNotDefinitelyAssignedVariables(
184 preSwitchInitStateIndex);
188 statement.generateCode(scope, codeStream);
191 // place the trailing labels (for break and default case)
193 if (defaultCase == null) {
194 defaultLabel.place();
196 // May loose some local variable initializations : affecting the local variable attributes
197 if (mergedInitStateIndex != -1) {
198 codeStream.removeNotDefinitelyAssignedVariables(
200 mergedInitStateIndex);
201 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
203 if (scope != currentScope) {
204 codeStream.exitUserScope(scope);
206 codeStream.recordPositionsFrom(pc, this.sourceStart);
208 if (this.scope != null) this.scope.switchCase = null; // no longer inside switch case block
212 public StringBuffer printStatement(int indent, StringBuffer output) {
214 printIndent(indent, output).append("switch ("); //$NON-NLS-1$
215 expression.printExpression(0, output).append(") {"); //$NON-NLS-1$
216 if (statements != null) {
217 for (int i = 0; i < statements.length; i++) {
219 if (statements[i] instanceof CaseStatement) {
220 statements[i].printStatement(indent, output);
222 statements[i].printStatement(indent+2, output);
226 output.append("\n"); //$NON-NLS-1$
227 return printIndent(indent, output).append('}');
230 public void resolve(BlockScope upperScope) {
233 TypeBinding testType = expression.resolveType(upperScope);
234 if (testType == null)
236 expression.implicitWidening(testType, testType);
237 if (!(expression.isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
238 if (!testType.isCompatibleWith(IntBinding)) {
239 upperScope.problemReporter().incorrectSwitchType(expression, testType);
243 if (statements != null) {
244 scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
246 // collection of cases is too big but we will only iterate until caseCount
247 cases = new CaseStatement[length = statements.length];
248 int[] casesValues = new int[length];
249 CaseStatement[] duplicateCaseStatements = null;
250 int duplicateCaseStatementsCounter = 0;
252 for (int i = 0; i < length; i++) {
254 final Statement statement = statements[i];
255 if ((constant = statement.resolveCase(scope, testType, this)) != null) {
256 //----check for duplicate case statement------------
257 if (constant != NotAConstant) {
258 int key = constant.intValue();
259 for (int j = 0; j < counter; j++) {
260 if (casesValues[j] == key) {
261 final CaseStatement currentCaseStatement = (CaseStatement) statement;
262 if (duplicateCaseStatements == null) {
263 scope.problemReporter().duplicateCase(cases[j]);
264 scope.problemReporter().duplicateCase(currentCaseStatement);
265 duplicateCaseStatements = new CaseStatement[length];
266 duplicateCaseStatements[duplicateCaseStatementsCounter++] = cases[j];
267 duplicateCaseStatements[duplicateCaseStatementsCounter++] = currentCaseStatement;
269 boolean found = false;
270 searchReportedDuplicate: for (int k = 2; k < duplicateCaseStatementsCounter; k++) {
271 if (duplicateCaseStatements[k] == statement) {
273 break searchReportedDuplicate;
277 scope.problemReporter().duplicateCase(currentCaseStatement);
278 duplicateCaseStatements[duplicateCaseStatementsCounter++] = currentCaseStatement;
283 casesValues[counter++] = key;
288 if ((this.bits & UndocumentedEmptyBlockMASK) != 0) {
289 upperScope.problemReporter().undocumentedEmptyBlock(this.blockStart, this.sourceEnd);
293 if (this.scope != null) this.scope.switchCase = null; // no longer inside switch case block
297 public void traverse(
299 BlockScope blockScope) {
301 if (visitor.visit(this, blockScope)) {
302 expression.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);