removed Makefile; lifted repo/org.ibex.tool/src/ to src/
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / SwitchStatement.java
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
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.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.*;
19
20 public class SwitchStatement extends Statement {
21
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;
31         
32         // for local variables table attributes
33         int preSwitchInitStateIndex = -1;
34         int mergedInitStateIndex = -1;
35
36         public FlowInfo analyseCode(
37                         BlockScope currentScope,
38                         FlowContext flowContext,
39                         FlowInfo flowInfo) {
40
41             try {
42                         flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo);
43                         SwitchFlowContext switchContext =
44                                 new SwitchFlowContext(flowContext, this, (breakLabel = new Label()));
45         
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);
52                         int caseIndex = 0;
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
59                                                 caseIndex++;
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
66                                         }
67                                         if (!statement.complainIfUnreachable(caseInits, scope, didAlreadyComplain)) {
68                                                 caseInits = statement.analyseCode(scope, switchContext, caseInits);
69                                         } else {
70                                                 didAlreadyComplain = true;
71                                         }
72                                 }
73                         }
74         
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);
82                                 return flowInfo;
83                         }
84         
85                         // merge all branches inits
86                         FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
87                         mergedInitStateIndex =
88                                 currentScope.methodScope().recordInitializationStates(mergedInfo);
89                         return mergedInfo;
90             } finally {
91                 if (this.scope != null) this.scope.switchCase = null; // no longer inside switch case block
92             }
93         }
94
95         /**
96          * Switch code generation
97          *
98          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
99          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
100          */
101         public void generateCode(BlockScope currentScope, CodeStream codeStream) {
102
103             try {
104                         int[] sortedIndexes = new int[caseCount];
105                         int[] localKeysCopy;
106                         if ((bits & IsReachableMASK) == 0) {
107                                 return;
108                         }
109                         int pc = codeStream.position;
110         
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));
119                         }
120         
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;
124                         }
125                         System.arraycopy(
126                                 constants,
127                                 0,
128                                 (localKeysCopy = new int[caseCount]),
129                                 0,
130                                 caseCount);
131                         CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes);
132                         CaseLabel defaultLabel = new CaseLabel(codeStream);
133                         if (defaultCase != null) {
134                                 defaultCase.targetLabel = defaultLabel;
135                         }
136                         // generate expression testes
137                         expression.generateCode(currentScope, codeStream, needSwitch);
138         
139                         // generate the appropriate switch table/lookup bytecode
140                         if (needSwitch) {
141                                 int max = localKeysCopy[caseCount - 1];
142                                 int min = localKeysCopy[0];
143                                 if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
144                                         
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);
149         
150                                         } else {
151                                                 codeStream.tableswitch(
152                                                         defaultLabel,
153                                                         min,
154                                                         max,
155                                                         constants,
156                                                         sortedIndexes,
157                                                         caseLabels);
158                                         }
159                                 } else {
160                                         codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
161                                 }
162                                 codeStream.updateLastRecordedEndPC(codeStream.position);
163                         }
164                         
165                         // generate the switch block statements
166                         int caseIndex = 0;
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(
174                                                                 currentScope,
175                                                                 preSwitchInitStateIndex);
176                                                 }
177                                                 caseIndex++;
178                                         } else {
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(
183                                                                         currentScope,
184                                                                         preSwitchInitStateIndex);
185                                                         }
186                                                 }
187                                         }
188                                         statement.generateCode(scope, codeStream);
189                                 }
190                         }
191                         // place the trailing labels (for break and default case)
192                         breakLabel.place();
193                         if (defaultCase == null) {
194                                 defaultLabel.place();
195                         }
196                         // May loose some local variable initializations : affecting the local variable attributes
197                         if (mergedInitStateIndex != -1) {
198                                 codeStream.removeNotDefinitelyAssignedVariables(
199                                         currentScope,
200                                         mergedInitStateIndex);
201                                 codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
202                         }
203                         if (scope != currentScope) {
204                                 codeStream.exitUserScope(scope);
205                         }
206                         codeStream.recordPositionsFrom(pc, this.sourceStart);
207             } finally {
208                 if (this.scope != null) this.scope.switchCase = null; // no longer inside switch case block
209             }           
210         }
211
212         public StringBuffer printStatement(int indent, StringBuffer output) {
213
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++) {
218                                 output.append('\n');
219                                 if (statements[i] instanceof CaseStatement) {
220                                         statements[i].printStatement(indent, output);
221                                 } else {
222                                         statements[i].printStatement(indent+2, output);
223                                 }
224                         }
225                 }
226                 output.append("\n"); //$NON-NLS-1$
227                 return printIndent(indent, output).append('}');
228         }
229
230         public void resolve(BlockScope upperScope) {
231         
232             try {
233                         TypeBinding testType = expression.resolveType(upperScope);
234                         if (testType == null)
235                                 return;
236                         expression.implicitWidening(testType, testType);
237                         if (!(expression.isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
238                                 if (!testType.isCompatibleWith(IntBinding)) {
239                                         upperScope.problemReporter().incorrectSwitchType(expression, testType);
240                                         return;
241                                 }
242                         }
243                         if (statements != null) {
244                                 scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
245                                 int length;
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;
251                                 int counter = 0;
252                                 for (int i = 0; i < length; i++) {
253                                         Constant constant;
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;
268                                                                         } else {
269                                                                                 boolean found = false;
270                                                                                 searchReportedDuplicate: for (int k = 2; k < duplicateCaseStatementsCounter; k++) {
271                                                                                         if (duplicateCaseStatements[k] == statement) {
272                                                                                                 found = true;
273                                                                                                 break searchReportedDuplicate;
274                                                                                         }
275                                                                                 }
276                                                                                 if (!found) {
277                                                                                         scope.problemReporter().duplicateCase(currentCaseStatement);
278                                                                                         duplicateCaseStatements[duplicateCaseStatementsCounter++] = currentCaseStatement;
279                                                                                 }
280                                                                         }
281                                                                 }
282                                                         }
283                                                         casesValues[counter++] = key;
284                                                 }
285                                         }
286                                 }
287                         } else {
288                                 if ((this.bits & UndocumentedEmptyBlockMASK) != 0) {
289                                         upperScope.problemReporter().undocumentedEmptyBlock(this.blockStart, this.sourceEnd);
290                                 }
291                         }
292             } finally {
293                 if (this.scope != null) this.scope.switchCase = null; // no longer inside switch case block
294             }
295         }
296
297         public void traverse(
298                         ASTVisitor visitor,
299                         BlockScope blockScope) {
300
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);
307                         }
308                 }
309                 visitor.endVisit(this, blockScope);
310         }
311         
312         /**
313          * Dispatch the call on its last statement.
314          */
315         public void branchChainTo(Label label) {
316                 
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);
324                 }
325         }
326 }