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 18561a4..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,18 +1,16 @@ /******************************************************************************* - * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. + * 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 v0.5 + * 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-v05.html + * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation - ******************************************************************************/ + *******************************************************************************/ package net.sourceforge.phpdt.internal.compiler.ast; -import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor; -import net.sourceforge.phpdt.internal.compiler.codegen.CaseLabel; -import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream; +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; @@ -22,210 +20,280 @@ import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding; public class SwitchStatement extends Statement { - public Expression testExpression; + public Expression expression; + public Statement[] statements; + public BlockScope scope; + public int explicitDeclarations; + public Label breakLabel; - public Case[] cases; + + 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 = testExpression.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 + 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.DeadEnd; + FlowInfo caseInits = FlowInfo.DEAD_END; // in case of statements before the first case - preSwitchInitStateIndex = - currentScope.methodScope().recordInitializationStates(flowInfo); + 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])) { // statements[i] is a case or a default case + if ((caseIndex < caseCount) && (statement == cases[caseIndex])) { // statement + // is a + // case caseIndex++; - caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits()); - } else { - if (statement == defaultCase) { - caseInits = caseInits.mergedWith(flowInfo.copy().unconditionalInits()); - } + 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)) { - caseInits = statement.analyseCode(scope, switchContext, caseInits); + 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 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); + 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); + mergedInitStateIndex = currentScope.methodScope() + .recordInitializationStates(mergedInfo); return mergedInfo; } + /** * Switch code generation - * - * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope - * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream + * + * @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; + // 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); + // } - // 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)); + public void resetStateForCodeGeneration() { + if (this.breakLabel != null) { + this.breakLabel.resetStateForCodeGeneration(); } + } - // 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); + public StringBuffer printStatement(int indent, StringBuffer output) { - // generate the appropriate switch table - if (needSwitch) { - int max = localKeysCopy[caseCount - 1]; - int min = localKeysCopy[0]; - if ((long) (caseCount * 2.5) > ((long) max - (long) min)) { - 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; + printIndent(indent, output).append("switch ("); //$NON-NLS-1$ + expression.printExpression(0, output).append(") {"); //$NON-NLS-1$ 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++; - } + for (int i = 0; i < statements.length; i++) { + output.append('\n'); + if (statements[i] instanceof CaseStatement) { + statements[i].printStatement(indent, output); } else { - if (statement == defaultCase) { // statements[i] is a case or a default case - if (preSwitchInitStateIndex != -1) { - codeStream.removeNotDefinitelyAssignedVariables( - currentScope, - preSwitchInitStateIndex); - } - } + statements[i].printStatement(indent + 2, output); } - 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() { - - this.breakLabel.resetStateForCodeGeneration(); + output.append("\n"); //$NON-NLS-1$ + return printIndent(indent, output).append('}'); } public void resolve(BlockScope upperScope) { - TypeBinding testType = testExpression.resolveType(upperScope); + TypeBinding testType = expression.resolveType(upperScope); if (testType == null) return; - testExpression.implicitWidening(testType, testType); - if (!(testExpression - .isConstantValueOfTypeAssignableToType(testType, IntBinding))) { - if (!BlockScope.areTypesCompatible(testType, IntBinding)) { - upperScope.problemReporter().incorrectSwitchType(testExpression, testType); + 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); + scope = explicitDeclarations == 0 ? upperScope : new BlockScope( + upperScope); int length; - // collection of cases is too big but we will only iterate until caseCount - cases = new Case[length = statements.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------------ + // ----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((Case) statements[i], cst); + scope.problemReporter().duplicateCase( + (CaseStatement) statements[i], cst); // TODO: + // (philippe) + // could + // improve + // diagnosis + // to + // indicate + // colliding + // case } } casesValues[counter++] = key; @@ -234,44 +302,46 @@ public class SwitchStatement extends Statement { } } } + public String toString(int tab) { String inFront, s = tabString(tab); inFront = s; - s = s + "switch (" + testExpression.toStringExpression() + ") "; //$NON-NLS-1$ //$NON-NLS-2$ + 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$ + 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. + // 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 Break) + 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 Case) - || (statements[i] instanceof DefaultCase)) { + // ============= + if ((statements[i] instanceof CaseStatement) + || (statements[i] instanceof DefaultCase)) { i++; - while (!((statements[i] instanceof Case) - || (statements[i] instanceof DefaultCase))) { - if ((statements[i] instanceof Expression) || (statements[i] instanceof Break)) + 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$ + s = s + + "\n" + statements[i].toString(tab + 6) + " ; "; //$NON-NLS-1$ //$NON-NLS-2$ i++; } } else { @@ -280,17 +350,16 @@ public class SwitchStatement extends Statement { } } } catch (IndexOutOfBoundsException e) { - }; + } + ; s = s + "}"; //$NON-NLS-1$ return s; } - public void traverse( - IAbstractSyntaxTreeVisitor visitor, - BlockScope blockScope) { + public void traverse(ASTVisitor visitor, BlockScope blockScope) { if (visitor.visit(this, blockScope)) { - testExpression.traverse(visitor, scope); + expression.traverse(visitor, scope); if (statements != null) { int statementsLength = statements.length; for (int i = 0; i < statementsLength; i++) @@ -299,12 +368,12 @@ public class SwitchStatement extends Statement { } 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 @@ -314,4 +383,4 @@ public class SwitchStatement extends Statement { label.appendForwardReferencesFrom(this.breakLabel); } } -} \ No newline at end of file +}