X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/SwitchStatement.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/SwitchStatement.java index b1a0cda..55cdca7 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/SwitchStatement.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/SwitchStatement.java @@ -1,68 +1,386 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ package net.sourceforge.phpdt.internal.compiler.ast; -import java.util.List; -import java.util.ArrayList; +import net.sourceforge.phpdt.internal.compiler.ASTVisitor; +import net.sourceforge.phpdt.internal.compiler.codegen.Label; +import net.sourceforge.phpdt.internal.compiler.flow.FlowContext; +import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo; +import net.sourceforge.phpdt.internal.compiler.flow.SwitchFlowContext; +import net.sourceforge.phpdt.internal.compiler.impl.Constant; +import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; +import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding; -/** - * @author Matthieu Casanova - */ public class SwitchStatement extends Statement { + public Expression expression; - public Expression variable; - public AbstractCase[] cases; - - public SwitchStatement(final Expression variable, - final AbstractCase[] cases, - final int sourceStart, - final int sourceEnd) { - super(sourceStart, sourceEnd); - this.variable = variable; - this.cases = cases; - } - - /** - * Return the object into String. - * @param tab how many tabs (not used here - * @return a String - */ - public String toString(final int tab) { - final StringBuffer buff = new StringBuffer(tabString(tab)); - buff.append("switch (").append(variable.toStringExpression()).append(") {\n"); - for (int i = 0; i < cases.length; i++) { - final AbstractCase cas = cases[i]; - buff.append(cas.toString(tab + 1)); - buff.append('\n'); - } - buff.append('}'); - return buff.toString(); - } - - /** - * Get the variables from outside (parameters, globals ...) - */ - public void getOutsideVariable(final List list) { - for (int i = 0; i < cases.length; i++) { - cases[i].getOutsideVariable(list); - } - } - - /** - * get the modified variables. - */ - public void getModifiedVariable(final List list) { - for (int i = 0; i < cases.length; i++) { - cases[i].getModifiedVariable(list); - } - variable.getModifiedVariable(list); - } - - /** - * Get the variables used. - */ - public void getUsedVariable(final List list) { - for (int i = 0; i < cases.length; i++) { - cases[i].getUsedVariable(list); - } - variable.getUsedVariable(list); - } + public Statement[] statements; + + public BlockScope scope; + + public int explicitDeclarations; + + public Label breakLabel; + + public CaseStatement[] cases; + + public DefaultCase defaultCase; + + public int caseCount = 0; + + // for local variables table attributes + int preSwitchInitStateIndex = -1; + + int mergedInitStateIndex = -1; + + /** + * SwitchStatement constructor comment. + */ + public SwitchStatement() { + super(); + } + + public FlowInfo analyseCode(BlockScope currentScope, + FlowContext flowContext, FlowInfo flowInfo) { + flowInfo = expression.analyseCode(currentScope, flowContext, flowInfo); + SwitchFlowContext switchContext = new SwitchFlowContext(flowContext, + this, (breakLabel = new Label())); + + // analyse the block by considering specially the case/default + // statements (need to bind them + // to the entry point) + FlowInfo caseInits = FlowInfo.DEAD_END; + // in case of statements before the first case + preSwitchInitStateIndex = currentScope.methodScope() + .recordInitializationStates(flowInfo); + int caseIndex = 0; + if (statements != null) { + boolean didAlreadyComplain = false; + for (int i = 0, max = statements.length; i < max; i++) { + Statement statement = statements[i]; + if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement + // is a + // case + caseIndex++; + caseInits = caseInits.mergedWith(flowInfo.copy() + .unconditionalInits()); + didAlreadyComplain = false; // reset complaint + } else if (statement == defaultCase) { // statement is the + // default case + caseInits = caseInits.mergedWith(flowInfo.copy() + .unconditionalInits()); + didAlreadyComplain = false; // reset complaint + } + if (!caseInits.complainIfUnreachable(statement, scope, + didAlreadyComplain)) { + caseInits = statement.analyseCode(scope, switchContext, + caseInits); + } else { + didAlreadyComplain = true; + } + } + } + + // if no default case, then record it may jump over the block directly + // to the end + if (defaultCase == null) { + // only retain the potential initializations + flowInfo.addPotentialInitializationsFrom(caseInits + .mergedWith(switchContext.initsOnBreak)); + mergedInitStateIndex = currentScope.methodScope() + .recordInitializationStates(flowInfo); + return flowInfo; + } + + // merge all branches inits + FlowInfo mergedInfo = caseInits.mergedWith(switchContext.initsOnBreak); + mergedInitStateIndex = currentScope.methodScope() + .recordInitializationStates(mergedInfo); + return mergedInfo; + } + + /** + * Switch code generation + * + * @param currentScope + * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope + * @param codeStream + * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream + */ + // public void generateCode(BlockScope currentScope, CodeStream codeStream) + // { + // int[] sortedIndexes = new int[caseCount]; + // int[] localKeysCopy; + // if ((bits & IsReachableMASK) == 0) { + // return; + // } + // int pc = codeStream.position; + // + // // prepare the labels and constants + // breakLabel.codeStream = codeStream; + // CaseLabel[] caseLabels = new CaseLabel[caseCount]; + // int[] constants = new int[caseCount]; + // boolean needSwitch = caseCount != 0; + // for (int i = 0; i < caseCount; i++) { + // constants[i] = cases[i].constantExpression.constant.intValue(); + // cases[i].targetLabel = (caseLabels[i] = new CaseLabel(codeStream)); + // } + // + // // we sort the keys to be able to generate the code for tableswitch or + // lookupswitch + // for (int i = 0; i < caseCount; i++) { + // sortedIndexes[i] = i; + // } + // System.arraycopy( + // constants, + // 0, + // (localKeysCopy = new int[caseCount]), + // 0, + // caseCount); + // CodeStream.sort(localKeysCopy, 0, caseCount - 1, sortedIndexes); + // CaseLabel defaultLabel = new CaseLabel(codeStream); + // if (defaultCase != null) { + // defaultCase.targetLabel = defaultLabel; + // } + // // generate expression testes + // testExpression.generateCode(currentScope, codeStream, needSwitch); + // + // // generate the appropriate switch table/lookup bytecode + // if (needSwitch) { + // int max = localKeysCopy[caseCount - 1]; + // int min = localKeysCopy[0]; + // if ((long) (caseCount * 2.5) > ((long) max - (long) min)) { + // + // // work-around 1.3 VM bug, if max>0x7FFF0000, must use lookup bytecode + // // see http://dev.eclipse.org/bugs/show_bug.cgi?id=21557 + // if (max > 0x7FFF0000 && + // currentScope.environment().options.complianceLevel < + // CompilerOptions.JDK1_4) { + // codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, + // caseLabels); + // + // } else { + // codeStream.tableswitch( + // defaultLabel, + // min, + // max, + // constants, + // sortedIndexes, + // caseLabels); + // } + // } else { + // codeStream.lookupswitch(defaultLabel, constants, sortedIndexes, + // caseLabels); + // } + // codeStream.updateLastRecordedEndPC(codeStream.position); + // } + // + // // generate the switch block statements + // int caseIndex = 0; + // if (statements != null) { + // for (int i = 0, maxCases = statements.length; i < maxCases; i++) { + // Statement statement = statements[i]; + // if ((caseIndex < caseCount) + // && (statement == cases[caseIndex])) { // statements[i] is a case + // if (preSwitchInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // preSwitchInitStateIndex); + // caseIndex++; + // } + // } else { + // if (statement == defaultCase) { // statements[i] is a case or a default + // case + // if (preSwitchInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // preSwitchInitStateIndex); + // } + // } + // } + // statement.generateCode(scope, codeStream); + // } + // } + // // place the trailing labels (for break and default case) + // breakLabel.place(); + // if (defaultCase == null) { + // defaultLabel.place(); + // } + // // May loose some local variable initializations : affecting the local + // variable attributes + // if (mergedInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // mergedInitStateIndex); + // codeStream.addDefinitelyAssignedVariables(currentScope, + // mergedInitStateIndex); + // } + // if (scope != currentScope) { + // codeStream.exitUserScope(scope); + // } + // codeStream.recordPositionsFrom(pc, this.sourceStart); + // } + + public void resetStateForCodeGeneration() { + if (this.breakLabel != null) { + this.breakLabel.resetStateForCodeGeneration(); + } + } + + public StringBuffer printStatement(int indent, StringBuffer output) { + + printIndent(indent, output).append("switch ("); //$NON-NLS-1$ + expression.printExpression(0, output).append(") {"); //$NON-NLS-1$ + if (statements != null) { + for (int i = 0; i < statements.length; i++) { + output.append('\n'); + if (statements[i] instanceof CaseStatement) { + statements[i].printStatement(indent, output); + } else { + statements[i].printStatement(indent + 2, output); + } + } + } + output.append("\n"); //$NON-NLS-1$ + return printIndent(indent, output).append('}'); + } + + public void resolve(BlockScope upperScope) { + + TypeBinding testType = expression.resolveType(upperScope); + if (testType == null) + return; + expression.implicitWidening(testType, testType); + if (!(expression.isConstantValueOfTypeAssignableToType(testType, + IntBinding))) { + if (!testType.isCompatibleWith(IntBinding)) { + upperScope.problemReporter().incorrectSwitchType(expression, + testType); + return; + } + } + if (statements != null) { + scope = explicitDeclarations == 0 ? upperScope : new BlockScope( + upperScope); + int length; + // collection of cases is too big but we will only iterate until + // caseCount + cases = new CaseStatement[length = statements.length]; + int[] casesValues = new int[length]; + int counter = 0; + for (int i = 0; i < length; i++) { + Constant cst; + if ((cst = statements[i].resolveCase(scope, testType, this)) != null) { + // ----check for duplicate case statement------------ + if (cst != NotAConstant) { + int key = cst.intValue(); + for (int j = 0; j < counter; j++) { + if (casesValues[j] == key) { + scope.problemReporter().duplicateCase( + (CaseStatement) statements[i], cst); // TODO: + // (philippe) + // could + // improve + // diagnosis + // to + // indicate + // colliding + // case + } + } + casesValues[counter++] = key; + } + } + } + } + } + + public String toString(int tab) { + + String inFront, s = tabString(tab); + inFront = s; + s = s + "switch (" + expression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$ + if (statements == null) { + s = s + "{}"; //$NON-NLS-1$ + return s; + } else + s = s + "{"; //$NON-NLS-1$ + s = s + + (explicitDeclarations != 0 ? "// ---scope needed for " //$NON-NLS-1$ + + String.valueOf(explicitDeclarations) + + " locals------------ \n"//$NON-NLS-1$ + : "// ---NO scope needed------ \n"); //$NON-NLS-1$ + + int i = 0; + String tabulation = " "; //$NON-NLS-1$ + try { + while (true) { + // use instanceof in order not to polluate classes with behavior + // only needed for printing purpose. + if (statements[i] instanceof Expression) + s = s + "\n" + inFront + tabulation; //$NON-NLS-1$ + if (statements[i] instanceof BreakStatement) + s = s + statements[i].toString(0); + else + s = s + "\n" + statements[i].toString(tab + 2); //$NON-NLS-1$ + // ============= + if ((statements[i] instanceof CaseStatement) + || (statements[i] instanceof DefaultCase)) { + i++; + while (!((statements[i] instanceof CaseStatement) || (statements[i] instanceof DefaultCase))) { + if ((statements[i] instanceof Expression) + || (statements[i] instanceof BreakStatement)) + s = s + statements[i].toString(0) + " ; "; //$NON-NLS-1$ + else + s = s + + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$ + i++; + } + } else { + s = s + " ;"; //$NON-NLS-1$ + i++; + } + } + } catch (IndexOutOfBoundsException e) { + } + ; + s = s + "}"; //$NON-NLS-1$ + return s; + } + + public void traverse(ASTVisitor visitor, BlockScope blockScope) { + + if (visitor.visit(this, blockScope)) { + expression.traverse(visitor, scope); + if (statements != null) { + int statementsLength = statements.length; + for (int i = 0; i < statementsLength; i++) + statements[i].traverse(visitor, scope); + } + } + visitor.endVisit(this, blockScope); + } + + /** + * Dispatch the call on its last statement. + */ + public void branchChainTo(Label label) { + + // in order to improve debug attributes for stepping (11431) + // we want to inline the jumps to #breakLabel which already got + // generated (if any), and have them directly branch to a better + // location (the argument label). + // we know at this point that the breakLabel already got placed + if (this.breakLabel.hasForwardReferences()) { + label.appendForwardReferencesFrom(this.breakLabel); + } + } }