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.impl.*;
 
  15 import net.sourceforge.phpdt.internal.compiler.codegen.*;
 
  16 import net.sourceforge.phpdt.internal.compiler.flow.*;
 
  17 import net.sourceforge.phpdt.internal.compiler.lookup.*;
 
  19 public class SwitchStatement extends Statement {
 
  20         public Expression testExpression;
 
  21         public Statement[] statements;
 
  22         public BlockScope scope;
 
  23         public int explicitDeclarations;
 
  24         public Label breakLabel;
 
  26         public DefaultCase defaultCase;
 
  27         public int caseCount = 0;
 
  29         // for local variables table attributes
 
  30         int preSwitchInitStateIndex = -1;
 
  31         int mergedInitStateIndex = -1;
 
  33          * SwitchStatement constructor comment.
 
  35         public SwitchStatement() {
 
  38         public FlowInfo analyseCode(
 
  39                 BlockScope currentScope,
 
  40                 FlowContext flowContext,
 
  42                 flowInfo = testExpression.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.DeadEnd;
 
  49                 // in case of statements before the first case
 
  50                 preSwitchInitStateIndex =
 
  51                         currentScope.methodScope().recordInitializationStates(flowInfo);
 
  53                 if (statements != null) {
 
  54                         for (int i = 0, max = statements.length; i < max; i++) {
 
  55                                 Statement statement = statements[i];
 
  56                                 if ((caseIndex < caseCount)
 
  57                                         && (statement == cases[caseIndex])) { // statements[i] is a case or a default case
 
  59                                         caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
 
  61                                         if (statement == defaultCase) {
 
  62                                                 caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits());
 
  65                                 if (!caseInits.complainIfUnreachable(statement, scope)) {
 
  66                                         caseInits = statement.analyseCode(scope, switchContext, caseInits);
 
  71                 // if no default case, then record it may jump over the block directly to the end
 
  72                 if (defaultCase == null) {
 
  73                         // only retain the potential initializations
 
  74                         flowInfo.addPotentialInitializationsFrom(
 
  75                                 caseInits.mergedWith(switchContext.initsOnBreak));
 
  76                         mergedInitStateIndex =
 
  77                                 currentScope.methodScope().recordInitializationStates(flowInfo);
 
  81                 // merge all branches inits
 
  82                 FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
 
  83                 mergedInitStateIndex =
 
  84                         currentScope.methodScope().recordInitializationStates(mergedInfo);
 
  88          * Switch code generation
 
  90          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
 
  91          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
 
  93         public void generateCode(BlockScope currentScope, CodeStream codeStream) {
 
  94                 int[] sortedIndexes = new int[caseCount];
 
  96                 if ((bits & IsReachableMASK) == 0) {
 
  99                 int pc = codeStream.position;
 
 101                 // prepare the labels and constants
 
 102                 breakLabel.codeStream = codeStream;
 
 103                 CaseLabel[] caseLabels = new CaseLabel[caseCount];
 
 104                 int[] constants = new int[caseCount];
 
 105                 boolean needSwitch = caseCount != 0;
 
 106                 for (int i = 0; i < caseCount; i++) {
 
 107                         constants[i] = cases[i].constantExpression.constant.intValue();
 
 108                         cases[i].targetLabel = (caseLabels[i] = new CaseLabel(codeStream));
 
 111                 // we sort the keys to be able to generate the code for tableswitch or lookupswitch
 
 112                 for (int i = 0; i < caseCount; i++) {
 
 113                         sortedIndexes[i] = i;
 
 118                         (localKeysCopy = new int[caseCount]),
 
 121                 CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes);
 
 122                 CaseLabel defaultLabel = new CaseLabel(codeStream);
 
 123                 if (defaultCase != null) {
 
 124                         defaultCase.targetLabel = defaultLabel;
 
 126                 // generate expression testes
 
 127                 testExpression.generateCode(currentScope, codeStream, needSwitch);
 
 129                 // generate the appropriate switch table
 
 131                         int max = localKeysCopy[caseCount - 1];
 
 132                         int min = localKeysCopy[0];
 
 133                         if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
 
 134                                 codeStream.tableswitch(
 
 142                                 codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, caseLabels);
 
 144                         codeStream.updateLastRecordedEndPC(codeStream.position);
 
 146                 // generate the switch block statements
 
 148                 if (statements != null) {
 
 149                         for (int i = 0, maxCases = statements.length; i < maxCases; i++) {
 
 150                                 Statement statement = statements[i];
 
 151                                 if ((caseIndex < caseCount)
 
 152                                         && (statement == cases[caseIndex])) { // statements[i] is a case
 
 153                                         if (preSwitchInitStateIndex != -1) {
 
 154                                                 codeStream.removeNotDefinitelyAssignedVariables(
 
 156                                                         preSwitchInitStateIndex);
 
 160                                         if (statement == defaultCase) { // statements[i] is a case or a default case
 
 161                                                 if (preSwitchInitStateIndex != -1) {
 
 162                                                         codeStream.removeNotDefinitelyAssignedVariables(
 
 164                                                                 preSwitchInitStateIndex);
 
 168                                 statement.generateCode(scope, codeStream);
 
 171                 // place the trailing labels (for break and default case)
 
 173                 if (defaultCase == null) {
 
 174                         defaultLabel.place();
 
 176                 // May loose some local variable initializations : affecting the local variable attributes
 
 177                 if (mergedInitStateIndex != -1) {
 
 178                         codeStream.removeNotDefinitelyAssignedVariables(
 
 180                                 mergedInitStateIndex);
 
 181                         codeStream.addDefinitelyAssignedVariables(currentScope, mergedInitStateIndex);
 
 183                 if (scope != currentScope) {
 
 184                         codeStream.exitUserScope(scope);
 
 186                 codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 190         public void resetStateForCodeGeneration() {
 
 192                 this.breakLabel.resetStateForCodeGeneration();
 
 195         public void resolve(BlockScope upperScope) {
 
 197                 TypeBinding testType = testExpression.resolveType(upperScope);
 
 198                 if (testType == null)
 
 200                 testExpression.implicitWidening(testType, testType);
 
 202                         .isConstantValueOfTypeAssignableToType(testType, IntBinding))) {
 
 203                         if (!upperScope.areTypesCompatible(testType, IntBinding)) {
 
 204                                 upperScope.problemReporter().incorrectSwitchType(testExpression, testType);
 
 208                 if (statements != null) {
 
 209                         scope = explicitDeclarations == 0 ? upperScope : new BlockScope(upperScope);
 
 211                         // collection of cases is too big but we will only iterate until caseCount
 
 212                         cases = new Case[length = statements.length];
 
 213                         int[] casesValues = new int[length];
 
 215                         for (int i = 0; i < length; i++) {
 
 217                                 if ((cst = statements[i].resolveCase(scope, testType, this)) != null) {
 
 218                                         //----check for duplicate case statement------------
 
 219                                         if (cst != NotAConstant) {
 
 220                                                 int key = cst.intValue();
 
 221                                                 for (int j = 0; j < counter; j++) {
 
 222                                                         if (casesValues[j] == key) {
 
 223                                                                 scope.problemReporter().duplicateCase((Case) statements[i], cst);
 
 226                                                 casesValues[counter++] = key;
 
 232         public String toString(int tab) {
 
 234                 String inFront, s = tabString(tab);
 
 236                 s = s + "switch (" + testExpression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
 
 237                 if (statements == null) {
 
 238                         s = s + "{}"; //$NON-NLS-1$
 
 241                         s = s + "{"; //$NON-NLS-1$
 
 243                                         + (explicitDeclarations != 0
 
 244                                                 ? "// ---scope needed for " //$NON-NLS-1$
 
 245                                                         + String.valueOf(explicitDeclarations)
 
 246                                                         + " locals------------ \n"//$NON-NLS-1$
 
 247                                                 : "// ---NO scope needed------ \n"); //$NON-NLS-1$
 
 250                 String tabulation = "  "; //$NON-NLS-1$
 
 253                                 //use instanceof in order not to polluate classes with behavior only needed for printing purpose.
 
 254                                 if (statements[i] instanceof Expression)
 
 255                                         s = s + "\n" + inFront + tabulation; //$NON-NLS-1$
 
 256                                 if (statements[i] instanceof Break)
 
 257                                         s = s + statements[i].toString(0);
 
 259                                         s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
 
 261                                 if ((statements[i] instanceof Case)
 
 262                                         || (statements[i] instanceof DefaultCase)) {
 
 264                                         while (!((statements[i] instanceof Case)
 
 265                                                 || (statements[i] instanceof DefaultCase))) {
 
 266                                                 if ((statements[i] instanceof Expression) || (statements[i] instanceof Break))
 
 267                                                         s = s + statements[i].toString(0) + " ; "; //$NON-NLS-1$
 
 269                                                         s = s + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
 
 273                                         s = s + " ;"; //$NON-NLS-1$
 
 277                 } catch (IndexOutOfBoundsException e) {
 
 279                 s = s + "}"; //$NON-NLS-1$
 
 283         public void traverse(
 
 284                 IAbstractSyntaxTreeVisitor visitor,
 
 285                 BlockScope blockScope) {
 
 287                 if (visitor.visit(this, blockScope)) {
 
 288                         testExpression.traverse(visitor, scope);
 
 289                         if (statements != null) {
 
 290                                 int statementsLength = statements.length;
 
 291                                 for (int i = 0; i < statementsLength; i++)
 
 292                                         statements[i].traverse(visitor, scope);
 
 295                 visitor.endVisit(this, blockScope);
 
 299          * Dispatch the call on its last statement.
 
 301         public void branchChainTo(Label label) {
 
 303                 // in order to improve debug attributes for stepping (11431)
 
 304                 // we want to inline the jumps to #breakLabel which already got
 
 305                 // generated (if any), and have them directly branch to a better
 
 306                 // location (the argument label).
 
 307                 // we know at this point that the breakLabel already got placed
 
 308                 if (this.breakLabel.hasForwardReferences()) {
 
 309                         label.appendForwardReferencesFrom(this.breakLabel);