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.phpdt.internal.compiler.ast;
 
  13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
 
  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 expression;
 
  25         public Statement[] statements;
 
  27         public BlockScope scope;
 
  29         public int explicitDeclarations;
 
  31         public Label breakLabel;
 
  33         public CaseStatement[] cases;
 
  35         public DefaultCase defaultCase;
 
  37         public int caseCount = 0;
 
  39         // for local variables table attributes
 
  40         int preSwitchInitStateIndex = -1;
 
  42         int mergedInitStateIndex = -1;
 
  45          * SwitchStatement constructor comment.
 
  47         public SwitchStatement() {
 
  51         public FlowInfo analyseCode(BlockScope currentScope,
 
  52                         FlowContext flowContext, FlowInfo flowInfo) {
 
  53                 flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo);
 
  54                 SwitchFlowContext switchContext = new SwitchFlowContext(flowContext,
 
  55                                 this, (breakLabel = new Label()));
 
  57                 // analyse the block by considering specially the case/default
 
  58                 // statements (need to bind them
 
  59                 // to the entry point)
 
  60                 FlowInfo caseInits = FlowInfo.DEAD_END;
 
  61                 // in case of statements before the first case
 
  62                 preSwitchInitStateIndex = currentScope.methodScope()
 
  63                                 .recordInitializationStates(flowInfo);
 
  65                 if (statements != null) {
 
  66                         boolean didAlreadyComplain = false;
 
  67                         for (int i = 0, max = statements.length; i < max; i++) {
 
  68                                 Statement statement = statements[i];
 
  69                                 if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement
 
  73                                         caseInits = caseInits.mergedWith(flowInfo.copy()
 
  74                                                         .unconditionalInits());
 
  75                                         didAlreadyComplain = false; // reset complaint
 
  76                                 } else if (statement == defaultCase) { // statement is the
 
  78                                         caseInits = caseInits.mergedWith(flowInfo.copy()
 
  79                                                         .unconditionalInits());
 
  80                                         didAlreadyComplain = false; // reset complaint
 
  82                                 if (!caseInits.complainIfUnreachable(statement, scope,
 
  83                                                 didAlreadyComplain)) {
 
  84                                         caseInits = statement.analyseCode(scope, switchContext,
 
  87                                         didAlreadyComplain = true;
 
  92                 // if no default case, then record it may jump over the block directly
 
  94                 if (defaultCase == null) {
 
  95                         // only retain the potential initializations
 
  96                         flowInfo.addPotentialInitializationsFrom(caseInits
 
  97                                         .mergedWith(switchContext.initsOnBreak));
 
  98                         mergedInitStateIndex = currentScope.methodScope()
 
  99                                         .recordInitializationStates(flowInfo);
 
 103                 // merge all branches inits
 
 104                 FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak);
 
 105                 mergedInitStateIndex = currentScope.methodScope()
 
 106                                 .recordInitializationStates(mergedInfo);
 
 111          * Switch code generation
 
 113          * @param currentScope
 
 114          *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
 
 116          *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
 
 118         // public void generateCode(BlockScope currentScope, CodeStream codeStream)
 
 120         // int[] sortedIndexes = new int[caseCount];
 
 121         // int[] localKeysCopy;
 
 122         // if ((bits & IsReachableMASK) == 0) {
 
 125         // int pc = codeStream.position;
 
 127         // // prepare the labels and constants
 
 128         // breakLabel.codeStream = codeStream;
 
 129         // CaseLabel[] caseLabels = new CaseLabel[caseCount];
 
 130         // int[] constants = new int[caseCount];
 
 131         // boolean needSwitch = caseCount != 0;
 
 132         // for (int i = 0; i < caseCount; i++) {
 
 133         // constants[i] = cases[i].constantExpression.constant.intValue();
 
 134         // cases[i].targetLabel = (caseLabels[i] = new CaseLabel(codeStream));
 
 137         // // we sort the keys to be able to generate the code for tableswitch or
 
 139         // for (int i = 0; i < caseCount; i++) {
 
 140         // sortedIndexes[i] = i;
 
 145         // (localKeysCopy = new int[caseCount]),
 
 148         // CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes);
 
 149         // CaseLabel defaultLabel = new CaseLabel(codeStream);
 
 150         // if (defaultCase != null) {
 
 151         // defaultCase.targetLabel = defaultLabel;
 
 153         // // generate expression testes
 
 154         // testExpression.generateCode(currentScope, codeStream, needSwitch);
 
 156         // // generate the appropriate switch table/lookup bytecode
 
 158         // int max = localKeysCopy[caseCount - 1];
 
 159         // int min = localKeysCopy[0];
 
 160         // if ((long) (caseCount * 2.5) > ((long) max - (long) min)) {
 
 162         // // work-around 1.3 VM bug, if max>0x7FFF0000, must use lookup bytecode
 
 163         // // see http://dev.eclipse.org/bugs/show_bug.cgi?id=21557
 
 164         // if (max > 0x7FFF0000 &&
 
 165         // currentScope.environment().options.complianceLevel <
 
 166         // CompilerOptions.JDK1_4) {
 
 167         // codeStream.lookupswitch(defaultLabel, constants, sortedIndexes,
 
 171         // codeStream.tableswitch(
 
 180         // codeStream.lookupswitch(defaultLabel, constants, sortedIndexes,
 
 183         // codeStream.updateLastRecordedEndPC(codeStream.position);
 
 186         // // generate the switch block statements
 
 187         // int caseIndex = 0;
 
 188         // if (statements != null) {
 
 189         // for (int i = 0, maxCases = statements.length; i < maxCases; i++) {
 
 190         // Statement statement = statements[i];
 
 191         // if ((caseIndex < caseCount)
 
 192         // && (statement == cases[caseIndex])) { // statements[i] is a case
 
 193         // if (preSwitchInitStateIndex != -1) {
 
 194         // codeStream.removeNotDefinitelyAssignedVariables(
 
 196         // preSwitchInitStateIndex);
 
 200         // if (statement == defaultCase) { // statements[i] is a case or a default
 
 202         // if (preSwitchInitStateIndex != -1) {
 
 203         // codeStream.removeNotDefinitelyAssignedVariables(
 
 205         // preSwitchInitStateIndex);
 
 209         // statement.generateCode(scope, codeStream);
 
 212         // // place the trailing labels (for break and default case)
 
 213         // breakLabel.place();
 
 214         // if (defaultCase == null) {
 
 215         // defaultLabel.place();
 
 217         // // May loose some local variable initializations : affecting the local
 
 218         // variable attributes
 
 219         // if (mergedInitStateIndex != -1) {
 
 220         // codeStream.removeNotDefinitelyAssignedVariables(
 
 222         // mergedInitStateIndex);
 
 223         // codeStream.addDefinitelyAssignedVariables(currentScope,
 
 224         // mergedInitStateIndex);
 
 226         // if (scope != currentScope) {
 
 227         // codeStream.exitUserScope(scope);
 
 229         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 232         public void resetStateForCodeGeneration() {
 
 233                 if (this.breakLabel != null) {
 
 234                         this.breakLabel.resetStateForCodeGeneration();
 
 238         public StringBuffer printStatement(int indent, StringBuffer output) {
 
 240                 printIndent(indent, output).append("switch ("); //$NON-NLS-1$
 
 241                 expression.printExpression(0, output).append(") {"); //$NON-NLS-1$
 
 242                 if (statements != null) {
 
 243                         for (int i = 0; i < statements.length; i++) {
 
 245                                 if (statements[i] instanceof CaseStatement) {
 
 246                                         statements[i].printStatement(indent, output);
 
 248                                         statements[i].printStatement(indent + 2, output);
 
 252                 output.append("\n"); //$NON-NLS-1$
 
 253                 return printIndent(indent, output).append('}');
 
 256         public void resolve(BlockScope upperScope) {
 
 258                 TypeBinding testType = expression.resolveType(upperScope);
 
 259                 if (testType == null)
 
 261                 expression.implicitWidening(testType, testType);
 
 262                 if (!(expression.isConstantValueOfTypeAssignableToType(testType,
 
 264                         if (!testType.isCompatibleWith(IntBinding)) {
 
 265                                 upperScope.problemReporter().incorrectSwitchType(expression,
 
 270                 if (statements != null) {
 
 271                         scope = explicitDeclarations == 0 ? upperScope : new BlockScope(
 
 274                         // collection of cases is too big but we will only iterate until
 
 276                         cases = new CaseStatement[length = statements.length];
 
 277                         int[] casesValues = new int[length];
 
 279                         for (int i = 0; i < length; i++) {
 
 281                                 if ((cst = statements[i].resolveCase(scope, testType, this)) != null) {
 
 282                                         // ----check for duplicate case statement------------
 
 283                                         if (cst != NotAConstant) {
 
 284                                                 int key = cst.intValue();
 
 285                                                 for (int j = 0; j < counter; j++) {
 
 286                                                         if (casesValues[j] == key) {
 
 287                                                                 scope.problemReporter().duplicateCase(
 
 288                                                                                 (CaseStatement) statements[i], cst); // TODO:
 
 299                                                 casesValues[counter++] = key;
 
 306         public String toString(int tab) {
 
 308                 String inFront, s = tabString(tab);
 
 310                 s = s + "switch (" + expression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$
 
 311                 if (statements == null) {
 
 312                         s = s + "{}"; //$NON-NLS-1$
 
 315                         s = s + "{"; //$NON-NLS-1$
 
 317                                 + (explicitDeclarations != 0 ? "// ---scope needed for " //$NON-NLS-1$
 
 318                                                 + String.valueOf(explicitDeclarations)
 
 319                                                 + " locals------------ \n"//$NON-NLS-1$
 
 320                                 : "// ---NO scope needed------ \n"); //$NON-NLS-1$
 
 323                 String tabulation = "  "; //$NON-NLS-1$
 
 326                                 // use instanceof in order not to polluate classes with behavior
 
 327                                 // only needed for printing purpose.
 
 328                                 if (statements[i] instanceof Expression)
 
 329                                         s = s + "\n" + inFront + tabulation; //$NON-NLS-1$
 
 330                                 if (statements[i] instanceof BreakStatement)
 
 331                                         s = s + statements[i].toString(0);
 
 333                                         s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$
 
 335                                 if ((statements[i] instanceof CaseStatement)
 
 336                                                 || (statements[i] instanceof DefaultCase)) {
 
 338                                         while (!((statements[i] instanceof CaseStatement) || (statements[i] instanceof DefaultCase))) {
 
 339                                                 if ((statements[i] instanceof Expression)
 
 340                                                                 || (statements[i] instanceof BreakStatement))
 
 341                                                         s = s + statements[i].toString(0) + " ; "; //$NON-NLS-1$
 
 344                                                                         + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$
 
 348                                         s = s + " ;"; //$NON-NLS-1$
 
 352                 } catch (IndexOutOfBoundsException e) {
 
 355                 s = s + "}"; //$NON-NLS-1$
 
 359         public void traverse(ASTVisitor visitor, BlockScope blockScope) {
 
 361                 if (visitor.visit(this, blockScope)) {
 
 362                         expression.traverse(visitor, scope);
 
 363                         if (statements != null) {
 
 364                                 int statementsLength = statements.length;
 
 365                                 for (int i = 0; i < statementsLength; i++)
 
 366                                         statements[i].traverse(visitor, scope);
 
 369                 visitor.endVisit(this, blockScope);
 
 373          * Dispatch the call on its last statement.
 
 375         public void branchChainTo(Label label) {
 
 377                 // in order to improve debug attributes for stepping (11431)
 
 378                 // we want to inline the jumps to #breakLabel which already got
 
 379                 // generated (if any), and have them directly branch to a better
 
 380                 // location (the argument label).
 
 381                 // we know at this point that the breakLabel already got placed
 
 382                 if (this.breakLabel.hasForwardReferences()) {
 
 383                         label.appendForwardReferencesFrom(this.breakLabel);