KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > ast > SwitchStatement


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
15 import org.eclipse.jdt.internal.compiler.codegen.*;
16 import org.eclipse.jdt.internal.compiler.flow.*;
17 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
18 import org.eclipse.jdt.internal.compiler.impl.Constant;
19 import org.eclipse.jdt.internal.compiler.lookup.*;
20 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
21
22 public class SwitchStatement extends Statement {
23
24     public Expression expression;
25     public Statement[] statements;
26     public BlockScope scope;
27     public int explicitDeclarations;
28     public BranchLabel breakLabel;
29     public CaseStatement[] cases;
30     public CaseStatement defaultCase;
31     public int blockStart;
32     public int caseCount;
33     int[] constants;
34     
35     // fallthrough
36
public final static int CASE = 0;
37     public final static int FALLTHROUGH = 1;
38     public final static int ESCAPING = 2;
39     
40     
41     public SyntheticMethodBinding synthetic; // use for switch on enums types
42

43     // for local variables table attributes
44
int preSwitchInitStateIndex = -1;
45     int mergedInitStateIndex = -1;
46
47     public FlowInfo analyseCode(
48             BlockScope currentScope,
49             FlowContext flowContext,
50             FlowInfo flowInfo) {
51
52         try {
53             flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo);
54             SwitchFlowContext switchContext =
55                 new SwitchFlowContext(flowContext, this, (breakLabel = new BranchLabel()));
56     
57             // analyse the block by considering specially the case/default statements (need to bind them
58
// to the entry point)
59
FlowInfo caseInits = FlowInfo.DEAD_END;
60             // in case of statements before the first case
61
preSwitchInitStateIndex =
62                 currentScope.methodScope().recordInitializationStates(flowInfo);
63             int caseIndex = 0;
64             if (statements != null) {
65                 boolean didAlreadyComplain = false;
66                 int fallThroughState = CASE;
67                 for (int i = 0, max = statements.length; i < max; i++) {
68                     Statement statement = statements[i];
69                     if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement is a case
70
this.scope.enclosingCase = cases[caseIndex]; // record entering in a switch case block
71
caseIndex++;
72                         if (fallThroughState == FALLTHROUGH
73                                 && (statement.bits & ASTNode.DocumentedFallthrough) == 0) { // the case is not fall-through protected by a line comment
74
scope.problemReporter().possibleFallThroughCase(this.scope.enclosingCase);
75                         }
76                         caseInits = caseInits.mergedWith(flowInfo.unconditionalInits());
77                         didAlreadyComplain = false; // reset complaint
78
fallThroughState = CASE;
79                     } else if (statement == defaultCase) { // statement is the default case
80
this.scope.enclosingCase = defaultCase; // record entering in a switch case block
81
if (fallThroughState == FALLTHROUGH
82                                 && (statement.bits & ASTNode.DocumentedFallthrough) == 0) {
83                             scope.problemReporter().possibleFallThroughCase(this.scope.enclosingCase);
84                         }
85                         caseInits = caseInits.mergedWith(flowInfo.unconditionalInits());
86                         didAlreadyComplain = false; // reset complaint
87
fallThroughState = CASE;
88                     } else {
89                         fallThroughState = FALLTHROUGH; // reset below if needed
90
}
91                     if (!statement.complainIfUnreachable(caseInits, scope, didAlreadyComplain)) {
92                         caseInits = statement.analyseCode(scope, switchContext, caseInits);
93                         if (caseInits == FlowInfo.DEAD_END) {
94                             fallThroughState = ESCAPING;
95                         }
96                     } else {
97                         didAlreadyComplain = true;
98                     }
99                 }
100             }
101     
102             final TypeBinding resolvedTypeBinding = this.expression.resolvedType;
103             if (caseCount > 0 && resolvedTypeBinding.isEnum()) {
104                 final SourceTypeBinding sourceTypeBinding = this.scope.classScope().referenceContext.binding;
105                 this.synthetic = sourceTypeBinding.addSyntheticMethodForSwitchEnum(resolvedTypeBinding);
106             }
107             // if no default case, then record it may jump over the block directly to the end
108
if (defaultCase == null) {
109                 // only retain the potential initializations
110
flowInfo.addPotentialInitializationsFrom(
111                     caseInits.mergedWith(switchContext.initsOnBreak));
112                 mergedInitStateIndex =
113                     currentScope.methodScope().recordInitializationStates(flowInfo);
114                 return flowInfo;
115             }
116     
117             // merge all branches inits
118
FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
119             mergedInitStateIndex =
120                 currentScope.methodScope().recordInitializationStates(mergedInfo);
121             return mergedInfo;
122         } finally {
123             if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block
124
}
125     }
126
127     /**
128      * Switch code generation
129      *
130      * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
131      * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
132      */

133     public void generateCode(BlockScope currentScope, CodeStream codeStream) {
134
135         try {
136             if ((bits & IsReachable) == 0) {
137                 return;
138             }
139             int pc = codeStream.position;
140     
141             // prepare the labels and constants
142
this.breakLabel.initialize(codeStream);
143             CaseLabel[] caseLabels = new CaseLabel[this.caseCount];
144             boolean needSwitch = this.caseCount != 0;
145             for (int i = 0; i < caseCount; i++) {
146                 cases[i].targetLabel = (caseLabels[i] = new CaseLabel(codeStream));
147                 caseLabels[i].tagBits |= BranchLabel.USED;
148             }
149             CaseLabel defaultLabel = new CaseLabel(codeStream);
150             if (needSwitch) defaultLabel.tagBits |= BranchLabel.USED;
151             if (defaultCase != null) {
152                 defaultCase.targetLabel = defaultLabel;
153             }
154
155             final TypeBinding resolvedType = this.expression.resolvedType;
156             if (resolvedType.isEnum()) {
157                 if (needSwitch) {
158                     // go through the translation table
159
codeStream.invokestatic(this.synthetic);
160                     expression.generateCode(currentScope, codeStream, true);
161                     // get enum constant ordinal()
162
codeStream.invokeEnumOrdinal(resolvedType.constantPoolName());
163                     codeStream.iaload();
164                 } else {
165                     // no need to go through the translation table
166
expression.generateCode(currentScope, codeStream, false);
167                 }
168             } else {
169                 // generate expression
170
expression.generateCode(currentScope, codeStream, needSwitch); // value required (switch without cases)
171
}
172             // generate the appropriate switch table/lookup bytecode
173
if (needSwitch) {
174                 int[] sortedIndexes = new int[this.caseCount];
175                 // we sort the keys to be able to generate the code for tableswitch or lookupswitch
176
for (int i = 0; i < caseCount; i++) {
177                     sortedIndexes[i] = i;
178                 }
179                 int[] localKeysCopy;
180                 System.arraycopy(this.constants, 0, (localKeysCopy = new int[this.caseCount]), 0, this.caseCount);
181                 CodeStream.sort(localKeysCopy, 0, this.caseCount - 1, sortedIndexes);
182
183                 int max = localKeysCopy[this.caseCount - 1];
184                 int min = localKeysCopy[0];
185                 if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
186                     
187                     // work-around 1.3 VM bug, if max>0x7FFF0000, must use lookup bytecode
188
// see http://dev.eclipse.org/bugs/show_bug.cgi?id=21557
189
if (max > 0x7FFF0000 && currentScope.compilerOptions().complianceLevel < ClassFileConstants.JDK1_4) {
190                         codeStream.lookupswitch(defaultLabel, this.constants, sortedIndexes, caseLabels);
191     
192                     } else {
193                         codeStream.tableswitch(
194                             defaultLabel,
195                             min,
196                             max,
197                             this.constants,
198                             sortedIndexes,
199                             caseLabels);
200                     }
201                 } else {
202                     codeStream.lookupswitch(defaultLabel, this.constants, sortedIndexes, caseLabels);
203                 }
204                 codeStream.updateLastRecordedEndPC(this.scope, codeStream.position);
205             }
206             
207             // generate the switch block statements
208
int caseIndex = 0;
209             if (this.statements != null) {
210                 for (int i = 0, maxCases = this.statements.length; i < maxCases; i++) {
211                     Statement statement = this.statements[i];
212                     if ((caseIndex < this.caseCount) && (statement == this.cases[caseIndex])) { // statements[i] is a case
213
this.scope.enclosingCase = this.cases[caseIndex]; // record entering in a switch case block
214
if (preSwitchInitStateIndex != -1) {
215                             codeStream.removeNotDefinitelyAssignedVariables(currentScope, preSwitchInitStateIndex);
216                         }
217                         caseIndex++;
218                     } else {
219                         if (statement == this.defaultCase) { // statements[i] is a case or a default case
220
this.scope.enclosingCase = this.defaultCase; // record entering in a switch case block
221
if (preSwitchInitStateIndex != -1) {
222                                 codeStream.removeNotDefinitelyAssignedVariables(currentScope, preSwitchInitStateIndex);
223                             }
224                         }
225                     }
226                     statement.generateCode(scope, codeStream);
227                 }
228             }
229             // May loose some local variable initializations : affecting the local variable attributes
230
if (mergedInitStateIndex != -1) {
231                 codeStream.removeNotDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
232                 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
233             }
234             if (scope != currentScope) {
235                 codeStream.exitUserScope(this.scope);
236             }
237             // place the trailing labels (for break and default case)
238
this.breakLabel.place();
239             if (defaultCase == null) {
240                 // we want to force an line number entry to get an end position after the switch statement
241
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd, true);
242                 defaultLabel.place();
243             }
244             codeStream.recordPositionsFrom(pc, this.sourceStart);
245         } finally {
246             if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block
247
}
248     }
249
250     public StringBuffer JavaDoc printStatement(int indent, StringBuffer JavaDoc output) {
251
252         printIndent(indent, output).append("switch ("); //$NON-NLS-1$
253
expression.printExpression(0, output).append(") {"); //$NON-NLS-1$
254
if (statements != null) {
255             for (int i = 0; i < statements.length; i++) {
256                 output.append('\n');
257                 if (statements[i] instanceof CaseStatement) {
258                     statements[i].printStatement(indent, output);
259                 } else {
260                     statements[i].printStatement(indent+2, output);
261                 }
262             }
263         }
264         output.append("\n"); //$NON-NLS-1$
265
return printIndent(indent, output).append('}');
266     }
267
268     public void resolve(BlockScope upperScope) {
269     
270         try {
271             boolean isEnumSwitch = false;
272             TypeBinding expressionType = expression.resolveType(upperScope);
273             if (expressionType != null) {
274                 expression.computeConversion(upperScope, expressionType, expressionType);
275                 checkType: {
276                     if (expressionType.isBaseType()) {
277                         if (expression.isConstantValueOfTypeAssignableToType(expressionType, TypeBinding.INT))
278                             break checkType;
279                         if (expressionType.isCompatibleWith(TypeBinding.INT))
280                             break checkType;
281                     } else if (expressionType.isEnum()) {
282                         isEnumSwitch = true;
283                         break checkType;
284                     } else if (upperScope.isBoxingCompatibleWith(expressionType, TypeBinding.INT)) {
285                         expression.computeConversion(upperScope, TypeBinding.INT, expressionType);
286                         break checkType;
287                     }
288                     upperScope.problemReporter().incorrectSwitchType(expression, expressionType);
289                     expressionType = null; // fault-tolerance: ignore type mismatch from constants from hereon
290
}
291             }
292             if (statements != null) {
293                 scope = /*explicitDeclarations == 0 ? upperScope : */new BlockScope(upperScope);
294                 int length;
295                 // collection of cases is too big but we will only iterate until caseCount
296
cases = new CaseStatement[length = statements.length];
297                 this.constants = new int[length];
298                 CaseStatement[] duplicateCaseStatements = null;
299                 int duplicateCaseStatementsCounter = 0;
300                 int counter = 0;
301                 for (int i = 0; i < length; i++) {
302                     Constant constant;
303                     final Statement statement = statements[i];
304                     if ((constant = statement.resolveCase(scope, expressionType, this)) != Constant.NotAConstant) {
305                         int key = constant.intValue();
306                         //----check for duplicate case statement------------
307
for (int j = 0; j < counter; j++) {
308                             if (this.constants[j] == key) {
309                                 final CaseStatement currentCaseStatement = (CaseStatement) statement;
310                                 if (duplicateCaseStatements == null) {
311                                     scope.problemReporter().duplicateCase(cases[j]);
312                                     scope.problemReporter().duplicateCase(currentCaseStatement);
313                                     duplicateCaseStatements = new CaseStatement[length];
314                                     duplicateCaseStatements[duplicateCaseStatementsCounter++] = cases[j];
315                                     duplicateCaseStatements[duplicateCaseStatementsCounter++] = currentCaseStatement;
316                                 } else {
317                                     boolean found = false;
318                                     searchReportedDuplicate: for (int k = 2; k < duplicateCaseStatementsCounter; k++) {
319                                         if (duplicateCaseStatements[k] == statement) {
320                                             found = true;
321                                             break searchReportedDuplicate;
322                                         }
323                                     }
324                                     if (!found) {
325                                         scope.problemReporter().duplicateCase(currentCaseStatement);
326                                         duplicateCaseStatements[duplicateCaseStatementsCounter++] = currentCaseStatement;
327                                     }
328                                 }
329                             }
330                         }
331                         this.constants[counter++] = key;
332                     }
333                 }
334                 if (length != counter) { // resize constants array
335
System.arraycopy(this.constants, 0, this.constants = new int[counter], 0, counter);
336                 }
337             } else {
338                 if ((this.bits & UndocumentedEmptyBlock) != 0) {
339                     upperScope.problemReporter().undocumentedEmptyBlock(this.blockStart, this.sourceEnd);
340                 }
341             }
342             // for enum switch, check if all constants are accounted for (if no default)
343
if (isEnumSwitch && defaultCase == null
344                     && upperScope.compilerOptions().getSeverity(CompilerOptions.IncompleteEnumSwitch) != ProblemSeverities.Ignore) {
345                 int constantCount = this.constants == null ? 0 : this.constants.length; // could be null if no case statement
346
if (constantCount == caseCount // ignore diagnosis if unresolved constants
347
&& caseCount != ((ReferenceBinding)expressionType).enumConstantCount()) {
348                     FieldBinding[] enumFields = ((ReferenceBinding)expressionType.erasure()).fields();
349                     for (int i = 0, max = enumFields.length; i <max; i++) {
350                         FieldBinding enumConstant = enumFields[i];
351                         if ((enumConstant.modifiers & ClassFileConstants.AccEnum) == 0) continue;
352                         findConstant : {
353                             for (int j = 0; j < caseCount; j++) {
354                                 if ((enumConstant.id + 1) == this.constants[j]) // zero should not be returned see bug 141810
355
break findConstant;
356                             }
357                             // enum constant did not get referenced from switch
358
upperScope.problemReporter().missingEnumConstantCase(this, enumConstant);
359                         }
360                     }
361                 }
362             }
363         } finally {
364             if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block
365
}
366     }
367
368     public void traverse(
369             ASTVisitor visitor,
370             BlockScope blockScope) {
371
372         if (visitor.visit(this, blockScope)) {
373             expression.traverse(visitor, scope);
374             if (statements != null) {
375                 int statementsLength = statements.length;
376                 for (int i = 0; i < statementsLength; i++)
377                     statements[i].traverse(visitor, scope);
378             }
379         }
380         visitor.endVisit(this, blockScope);
381     }
382     
383     /**
384      * Dispatch the call on its last statement.
385      */

386     public void branchChainTo(BranchLabel label) {
387         
388         // in order to improve debug attributes for stepping (11431)
389
// we want to inline the jumps to #breakLabel which already got
390
// generated (if any), and have them directly branch to a better
391
// location (the argument label).
392
// we know at this point that the breakLabel already got placed
393
if (this.breakLabel.forwardReferenceCount() > 0) {
394             label.becomeDelegateFor(this.breakLabel);
395         }
396     }
397 }
398
Popular Tags