<s> package org . eclipse . jdt . internal . compiler . ast ; import java . util . Arrays ; import org . eclipse . jdt . internal . compiler . ASTVisitor ; import org . eclipse . jdt . internal . compiler . classfmt . ClassFileConstants ; import org . eclipse . jdt . internal . compiler . codegen . * ; import org . eclipse . jdt . internal . compiler . flow . * ; import org . eclipse . jdt . internal . compiler . impl . CompilerOptions ; import org . eclipse . jdt . internal . compiler . impl . Constant ; import org . eclipse . jdt . internal . compiler . lookup . * ; import org . eclipse . jdt . internal . compiler . problem . ProblemSeverities ; public class SwitchStatement extends Statement { public Expression expression ; public Statement [ ] statements ; public BlockScope scope ; public int explicitDeclarations ; public BranchLabel breakLabel ; public CaseStatement [ ] cases ; public CaseStatement defaultCase ; public int blockStart ; public int caseCount ; int [ ] constants ; String [ ] stringConstants ; public final static int CASE = 0 ; public final static int FALLTHROUGH = 1 ; public final static int ESCAPING = 2 ; private static final char [ ] SecretStringVariableName = "" . toCharArray ( ) ; public SyntheticMethodBinding synthetic ; int preSwitchInitStateIndex = - 1 ; int mergedInitStateIndex = - 1 ; CaseStatement [ ] duplicateCaseStatements = null ; int duplicateCaseStatementsCounter = 0 ; private LocalVariableBinding dispatchStringCopy = null ; public FlowInfo analyseCode ( BlockScope currentScope , FlowContext flowContext , FlowInfo flowInfo ) { try { flowInfo = this . expression . analyseCode ( currentScope , flowContext , flowInfo ) ; if ( ( this . expression . implicitConversion & TypeIds . UNBOXING ) != 0 || ( this . expression . resolvedType != null && this . expression . resolvedType . id == T_JavaLangString ) ) { this . expression . checkNPE ( currentScope , flowContext , flowInfo ) ; } SwitchFlowContext switchContext = new SwitchFlowContext ( flowContext , this , ( this . breakLabel = new BranchLabel ( ) ) ) ; FlowInfo caseInits = FlowInfo . DEAD_END ; this . preSwitchInitStateIndex = currentScope . methodScope ( ) . recordInitializationStates ( flowInfo ) ; int caseIndex = 0 ; if ( this . statements != null ) { int initialComplaintLevel = ( flowInfo . reachMode ( ) & FlowInfo . UNREACHABLE ) != 0 ? Statement . COMPLAINED_FAKE_REACHABLE : Statement . NOT_COMPLAINED ; int complaintLevel = initialComplaintLevel ; int fallThroughState = CASE ; for ( int i = 0 , max = this . statements . length ; i < max ; i ++ ) { Statement statement = this . statements [ i ] ; if ( ( caseIndex < this . caseCount ) && ( statement == this . cases [ caseIndex ] ) ) { this . scope . enclosingCase = this . cases [ caseIndex ] ; caseIndex ++ ; if ( fallThroughState == FALLTHROUGH && ( statement . bits & ASTNode . DocumentedFallthrough ) == 0 ) { this . scope . problemReporter ( ) . possibleFallThroughCase ( this . scope . enclosingCase ) ; } caseInits = caseInits . mergedWith ( flowInfo . unconditionalInits ( ) ) ; complaintLevel = initialComplaintLevel ; fallThroughState = CASE ; } else if ( statement == this . defaultCase ) { this . scope . enclosingCase = this . defaultCase ; if ( fallThroughState == FALLTHROUGH && ( statement . bits & ASTNode . DocumentedFallthrough ) == 0 ) { this . scope . problemReporter ( ) . possibleFallThroughCase ( this . scope . enclosingCase ) ; } caseInits = caseInits . mergedWith ( flowInfo . unconditionalInits ( ) ) ; complaintLevel = initialComplaintLevel ; fallThroughState = CASE ; } else { fallThroughState = FALLTHROUGH ; } if ( ( complaintLevel = statement . complainIfUnreachable ( caseInits , this . scope , complaintLevel ) ) < Statement . COMPLAINED_UNREACHABLE ) { caseInits = statement . analyseCode ( this . scope , switchContext , caseInits ) ; if ( caseInits == FlowInfo . DEAD_END ) { fallThroughState = ESCAPING ; } } } } final TypeBinding resolvedTypeBinding = this . expression . resolvedType ; if ( resolvedTypeBinding . isEnum ( ) ) { final SourceTypeBinding sourceTypeBinding = currentScope . classScope ( ) . referenceContext . binding ; this . synthetic = sourceTypeBinding . addSyntheticMethodForSwitchEnum ( resolvedTypeBinding ) ; } if ( this . defaultCase == null ) { flowInfo . addPotentialInitializationsFrom ( caseInits . mergedWith ( switchContext . initsOnBreak ) ) ; this . mergedInitStateIndex = currentScope . methodScope ( ) . recordInitializationStates ( flowInfo ) ; return flowInfo ; } FlowInfo mergedInfo = caseInits . mergedWith ( switchContext . initsOnBreak ) ; this . mergedInitStateIndex = currentScope . methodScope ( ) . recordInitializationStates ( mergedInfo ) ; return mergedInfo ; } finally { if ( this . scope != null ) this . scope . enclosingCase = null ; } } public void generateCodeForStringSwitch ( BlockScope currentScope , CodeStream codeStream ) { try { if ( ( this . bits & IsReachable ) == 0 ) { return ; } int pc = codeStream . position ; class StringSwitchCase implements Comparable { int hashCode ; String string ; BranchLabel label ; public StringSwitchCase ( int hashCode , String string , BranchLabel label ) { this . hashCode = hashCode ; this . string = string ; this . label = label ; } public int compareTo ( Object o ) { StringSwitchCase that = ( StringSwitchCase ) o ; if ( this . hashCode == that . hashCode ) { return 0 ; } if ( this . hashCode > that . hashCode ) { return 1 ; } return - 1 ; } public String toString ( ) { return "" + "case " + this . hashCode + ":(" + this . string + ")n" ; } } final boolean hasCases = this . caseCount != 0 ; StringSwitchCase [ ] stringCases = new StringSwitchCase [ this . caseCount ] ; BranchLabel [ ] sourceCaseLabels = new BranchLabel [ this . caseCount ] ; CaseLabel [ ] hashCodeCaseLabels = new CaseLabel [ this . caseCount ] ; this . constants = new int [ this . caseCount ] ; for ( int i = 0 , max = this . caseCount ; i < max ; i ++ ) { this . cases [ i ] . targetLabel = ( sourceCaseLabels [ i ] = new BranchLabel ( codeStream ) ) ; sourceCaseLabels [ i ] . tagBits |= BranchLabel . USED ; stringCases [ i ] = new StringSwitchCase ( this . stringConstants [ i ] . hashCode ( ) , this . stringConstants [ i ] , sourceCaseLabels [ i ] ) ; hashCodeCaseLabels [ i ] = new CaseLabel ( codeStream ) ; hashCodeCaseLabels [ i ] . tagBits |= BranchLabel . USED ; } Arrays . sort ( stringCases ) ; int uniqHashCount = 0 ; int lastHashCode = 0 ; for ( int i = 0 , length = this . caseCount ; i < length ; ++ i ) { int hashCode = stringCases [ i ] . hashCode ; if ( i == 0 || hashCode != lastHashCode ) { lastHashCode = this . constants [ uniqHashCount ++ ] = hashCode ; } } if ( uniqHashCount != this . caseCount ) { System . arraycopy ( this . constants , 0 , this . constants = new int [ uniqHashCount ] , 0 , uniqHashCount ) ; System . arraycopy ( hashCodeCaseLabels , 0 , hashCodeCaseLabels = new CaseLabel [ uniqHashCount ] , 0 , uniqHashCount ) ; } int [ ] sortedIndexes = new int [ uniqHashCount ] ; for ( int i = 0 ; i < uniqHashCount ; i ++ ) { sortedIndexes [ i ] = i ; } CaseLabel defaultCaseLabel = new CaseLabel ( codeStream ) ; defaultCaseLabel . tagBits |= BranchLabel . USED ; this . breakLabel . initialize ( codeStream ) ; BranchLabel defaultBranchLabel = new BranchLabel ( codeStream ) ; if ( hasCases ) defaultBranchLabel . tagBits |= BranchLabel . USED ; if ( this . defaultCase != null ) { this . defaultCase . targetLabel = defaultBranchLabel ; } this . expression . generateCode ( currentScope , codeStream , true ) ; codeStream . store ( this . dispatchStringCopy , true ) ; codeStream . addVariable ( this . dispatchStringCopy ) ; codeStream . invokeStringHashCode ( ) ; if ( hasCases ) { codeStream . lookupswitch ( defaultCaseLabel , this . constants , sortedIndexes , hashCodeCaseLabels ) ; for ( int i = 0 , j = 0 , max = this . caseCount ; i < max ; i ++ ) { int hashCode = stringCases [ i ] . hashCode ; if ( i == 0 || hashCode != lastHashCode ) { lastHashCode = hashCode ; if ( i != 0 ) { codeStream . goto_ ( defaultBranchLabel ) ; } hashCodeCaseLabels [ j ++ ] . place ( ) ; } codeStream . load ( this . dispatchStringCopy ) ; codeStream . ldc ( stringCases [ i ] . string ) ; codeStream . invokeStringEquals ( ) ; codeStream . ifne ( stringCases [ i ] . label ) ; } codeStream . goto_ ( defaultBranchLabel ) ; } else { codeStream . pop ( ) ; } int caseIndex = 0 ; if ( this . statements != null ) { for ( int i = 0 , maxCases = this . statements . length ; i < maxCases ; i ++ ) { Statement statement = this . statements [ i ] ; if ( ( caseIndex < this . caseCount ) && ( statement == this . cases [ caseIndex ] ) ) { this . scope . enclosingCase = this . cases [ caseIndex ] ; if ( this . preSwitchInitStateIndex != - 1 ) { codeStream . removeNotDefinitelyAssignedVariables ( currentScope , this . preSwitchInitStateIndex ) ; } caseIndex ++ ; } else { if ( statement == this . defaultCase ) { defaultCaseLabel . place ( ) ; this . scope . enclosingCase = this . defaultCase ; if ( this . preSwitchInitStateIndex != - 1 ) { codeStream . removeNotDefinitelyAssignedVariables ( currentScope , this . preSwitchInitStateIndex ) ; } } } statement . generateCode ( this . scope , codeStream ) ; } } if ( this . mergedInitStateIndex != - 1 ) { codeStream . removeNotDefinitelyAssignedVariables ( currentScope , this . mergedInitStateIndex ) ; codeStream . addDefinitelyAssignedVariables ( currentScope , this . mergedInitStateIndex ) ; } codeStream . removeVariable ( this . dispatchStringCopy ) ; if ( this . scope != currentScope ) { codeStream . exitUserScope ( this . scope ) ; } this . breakLabel . place ( ) ; if ( this . defaultCase == null ) { codeStream . recordPositionsFrom ( codeStream . position , this . sourceEnd , true ) ; defaultCaseLabel . place ( ) ; defaultBranchLabel . place ( ) ; } codeStream . recordPositionsFrom ( pc , this . sourceStart ) ; } catch ( Throwable e ) { e . printStackTrace ( ) ; } finally { if ( this . scope != null ) this . scope . enclosingCase = null ; } } public void generateCode ( BlockScope currentScope , CodeStream codeStream ) { if ( this . expression . resolvedType . id == TypeIds . T_JavaLangString ) { generateCodeForStringSwitch ( currentScope , codeStream ) ; return ; } try { if ( ( this . bits & IsReachable ) == 0 ) { return ; } int pc = codeStream . position ; this . breakLabel . initialize ( codeStream ) ; CaseLabel [ ] caseLabels = new CaseLabel [ this . caseCount ] ; for ( int i = 0 , max = this . caseCount ; i < max ; i ++ ) { this . cases [ i ] . targetLabel = ( caseLabels [ i ] = new CaseLabel ( codeStream ) ) ; caseLabels [ i ] . tagBits |= BranchLabel . USED ; } CaseLabel defaultLabel = new CaseLabel ( codeStream ) ; final boolean hasCases = this . caseCount != 0 ; if ( hasCases ) defaultLabel . tagBits |= BranchLabel . USED ; if ( this . defaultCase != null ) { this . defaultCase . targetLabel = defaultLabel ; } final TypeBinding resolvedType = this . expression . resolvedType ; boolean valueRequired = false ; if ( resolvedType . isEnum ( ) ) { codeStream . invoke ( Opcodes . OPC_invokestatic , this . synthetic , null ) ; this . expression . generateCode ( currentScope , codeStream , true ) ; codeStream . invokeEnumOrdinal ( resolvedType . constantPoolName ( ) ) ; codeStream . iaload ( ) ; if ( ! hasCases ) { codeStream . pop ( ) ; } } else { valueRequired = this . expression . constant == Constant . NotAConstant || hasCases ; this . expression . generateCode ( currentScope , codeStream , valueRequired ) ; } if ( hasCases ) { int [ ] sortedIndexes = new int [ this . caseCount ] ; for ( int i = 0 ; i < this . caseCount ; i ++ ) { sortedIndexes [ i ] = i ; } int [ ] localKeysCopy ; System . arraycopy ( this . constants , 0 , ( localKeysCopy = new int [ this . caseCount ] ) , 0 , this . caseCount ) ; CodeStream . sort ( localKeysCopy , 0 , this . caseCount - 1 , sortedIndexes ) ; int max = localKeysCopy [ this . caseCount - 1 ] ; int min = localKeysCopy [ 0 ] ; if ( ( long ) ( this . caseCount * 2.5 ) > ( ( long ) max - ( long ) min ) ) { if ( max > 0x7FFF0000 && currentScope . compilerOptions ( ) . complianceLevel < ClassFileConstants . JDK1_4 ) { codeStream . lookupswitch ( defaultLabel , this . constants , sortedIndexes , caseLabels ) ; } else { codeStream . tableswitch ( defaultLabel , min , max , this . constants , sortedIndexes , caseLabels ) ; } } else { codeStream . lookupswitch ( defaultLabel , this . constants , sortedIndexes , caseLabels ) ; } codeStream . updateLastRecordedEndPC ( this . scope , codeStream . position ) ; } else if ( valueRequired ) { codeStream . pop ( ) ; } int caseIndex = 0 ; if ( this . statements != null ) { for ( int i = 0 , maxCases = this . statements . length ; i < maxCases ; i ++ ) { Statement statement = this . statements [ i ] ; if ( ( caseIndex < this . caseCount ) && ( statement == this . cases [ caseIndex ] ) ) { this . scope . enclosingCase = this . cases [ caseIndex ] ; if ( this . preSwitchInitStateIndex != - 1 ) { codeStream . removeNotDefinitelyAssignedVariables ( currentScope , this . preSwitchInitStateIndex ) ; } caseIndex ++ ; } else { if ( statement == this . defaultCase ) { this . scope . enclosingCase = this . defaultCase ; if ( this . preSwitchInitStateIndex != - 1 ) { codeStream . removeNotDefinitelyAssignedVariables ( currentScope , this . preSwitchInitStateIndex ) ; } } } statement . generateCode ( this . scope , codeStream ) ; } } if ( this . mergedInitStateIndex != - 1 ) { codeStream . removeNotDefinitelyAssignedVariables ( currentScope , this . mergedInitStateIndex ) ; codeStream . addDefinitelyAssignedVariables ( currentScope , this . mergedInitStateIndex ) ; } if ( this . scope != currentScope ) { codeStream . exitUserScope ( this . scope ) ; } this . breakLabel . place ( ) ; if ( this . defaultCase == null ) { codeStream . recordPositionsFrom ( codeStream . position , this . sourceEnd , true ) ; defaultLabel . place ( ) ; } codeStream . recordPositionsFrom ( pc , this . sourceStart ) ; } finally { if ( this . scope != null ) this . scope . enclosingCase = null ; } } public StringBuffer printStatement ( int indent , StringBuffer output ) { printIndent ( indent , output ) . append ( "switch (" ) ; this . expression . printExpression ( 0 , output ) . append ( ") {" ) ; if ( this . statements != null ) { for ( int i = 0 ; i < this . statements . length ; i ++ ) { output . append ( '\n' ) ; if ( this . statements [ i ] instanceof CaseStatement ) { this . statements [ i ] . printStatement ( indent , output ) ; } else { this . statements [ i ] . printStatement ( indent + 2 , output ) ; } } } output . append ( "n" ) ; return printIndent ( indent , output ) . append ( '}' ) ; } public void resolve ( BlockScope upperScope ) { try { boolean isEnumSwitch = false ; boolean isStringSwitch = false ; TypeBinding expressionType = this . expression . resolveType ( upperScope ) ; if ( expressionType != null ) { this . expression . computeConversion ( upperScope , expressionType , expressionType ) ; checkType : { if ( ! expressionType . isValidBinding ( ) ) { expressionType = null ; break checkType ; } else if ( expressionType . isBaseType ( ) ) { if ( this . expression . isConstantValueOfTypeAssignableToType ( expressionType , TypeBinding . INT ) ) break checkType ; if ( expressionType . isCompatibleWith ( TypeBinding . INT ) ) break checkType ; } else if ( expressionType . isEnum ( ) ) { isEnumSwitch = true ; break checkType ; } else if ( upperScope . isBoxingCompatibleWith ( expressionType , TypeBinding . INT ) ) { this . expression . computeConversion ( upperScope , TypeBinding . INT , expressionType ) ; break checkType ; } else if ( upperScope . compilerOptions ( ) . complianceLevel >= ClassFileConstants . JDK1_7 && expressionType . id == TypeIds . T_JavaLangString ) { isStringSwitch = true ; break checkType ; } upperScope . problemReporter ( ) . incorrectSwitchType ( this . expression , expressionType ) ; expressionType = null ; } } if ( isStringSwitch ) { this . dispatchStringCopy = new LocalVariableBinding ( SecretStringVariableName , upperScope . getJavaLangString ( ) , ClassFileConstants . AccDefault , false ) ; upperScope . addLocalVariable ( this . dispatchStringCopy ) ; this . dispatchStringCopy . setConstant ( Constant . NotAConstant ) ; this . dispatchStringCopy . useFlag = LocalVariableBinding . USED ; } if ( this . statements != null ) { this . scope = new BlockScope ( upperScope ) ; int length ; this . cases = new CaseStatement [ length = this . statements . length ] ; if ( ! isStringSwitch ) { this . constants = new int [ length ] ; } else { this . stringConstants = new String [ length ] ; } int counter = 0 ; for ( int i = 0 ; i < length ; i ++ ) { Constant constant ; final Statement statement = this . statements [ i ] ; if ( ( constant = statement . resolveCase ( this . scope , expressionType , this ) ) != Constant . NotAConstant ) { if ( ! isStringSwitch ) { int key = constant . intValue ( ) ; for ( int j = 0 ; j < counter ; j ++ ) { if ( this . constants [ j ] == key ) { reportDuplicateCase ( ( CaseStatement ) statement , this . cases [ j ] , length ) ; } } this . constants [ counter ++ ] = key ; } else { String key = constant . stringValue ( ) ; for ( int j = 0 ; j < counter ; j ++ ) { if ( this . stringConstants [ j ] . equals ( key ) ) { reportDuplicateCase ( ( CaseStatement ) statement , this . cases [ j ] , length ) ; } } this . stringConstants [ counter ++ ] = key ; } } } if ( length != counter ) { if ( ! isStringSwitch ) { System . arraycopy ( this . constants , 0 , this . constants = new int [ counter ] , 0 , counter ) ; } else { System . arraycopy ( this . stringConstants , 0 , this . stringConstants = new String [ counter ] , 0 , counter ) ; } } } else { if ( ( this . bits & UndocumentedEmptyBlock ) != 0 ) { upperScope . problemReporter ( ) . undocumentedEmptyBlock ( this . blockStart , this . sourceEnd ) ; } } if ( isEnumSwitch && this . defaultCase == null && upperScope . compilerOptions ( ) . getSeverity ( CompilerOptions . IncompleteEnumSwitch ) != ProblemSeverities . Ignore ) { int constantCount = this . constants == null ? 0 : this . constants . length ; if ( constantCount == this . caseCount && this . caseCount != ( ( ReferenceBinding ) expressionType ) . enumConstantCount ( ) ) { FieldBinding [ ] enumFields = ( ( ReferenceBinding ) expressionType . erasure ( ) ) . fields ( ) ; for ( int i = 0 , max = enumFields . length ; i < max ; i ++ ) { FieldBinding enumConstant = enumFields [ i ] ; if ( ( enumConstant . modifiers & ClassFileConstants . AccEnum ) == 0 ) continue ; findConstant : { for ( int j = 0 ; j < this . caseCount ; j ++ ) { if ( ( enumConstant . id + 1 ) == this . constants [ j ] ) break findConstant ; } upperScope . problemReporter ( ) . missingEnumConstantCase ( this , enumConstant ) ; } } } } } finally { if ( this . scope != null ) this . scope . enclosingCase = null ; } } private void reportDuplicateCase ( final CaseStatement duplicate , final CaseStatement original , int length ) { if ( this . duplicateCaseStatements == null ) { this . scope . problemReporter ( ) . duplicateCase ( original ) ; this . scope . problemReporter ( ) . duplicateCase ( duplicate ) ; this . duplicateCaseStatements = new CaseStatement [ length ] ; this . duplicateCaseStatements [ this . duplicateCaseStatementsCounter ++ ] = original ; this . duplicateCaseStatements [ this . duplicateCaseStatementsCounter ++ ] = duplicate ; } else { boolean found = false ; searchReportedDuplicate : for ( int k = 2 ; k < this . duplicateCaseStatementsCounter ; k ++ ) { if ( this . duplicateCaseStatements [ k ] == duplicate ) { found = true ; break searchReportedDuplicate ; } } if ( ! found ) { this . scope . problemReporter ( ) . duplicateCase ( duplicate ) ; this . duplicateCaseStatements [ this . duplicateCaseStatementsCounter ++ ] = duplicate ; } } } public void traverse ( ASTVisitor visitor , BlockScope blockScope ) { if ( visitor . visit ( this , blockScope ) ) { this . expression . traverse ( visitor , blockScope ) ; if ( this . statements != null ) { int statementsLength = this . statements . length ; for ( int i = 0 ; i < statementsLength ; i ++ ) this . statements [ i ] . traverse ( visitor , this . scope ) ; } } visitor . endVisit ( this , blockScope ) ; } public void branchChainTo ( BranchLabel label ) { if ( this . breakLabel . forwardReferenceCount ( ) > 0 ) { label . becomeDelegateFor ( this . breakLabel ) ; } } } </s>