mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
6827009: Project Coin: Strings in Switch
Reviewed-by: jjg, mcimadamore
This commit is contained in:
parent
b967181a3f
commit
54d7808702
16 changed files with 886 additions and 8 deletions
|
@ -110,6 +110,9 @@ public enum Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allow encoding errors, giving only warnings. */
|
/** Allow encoding errors, giving only warnings. */
|
||||||
|
public boolean allowStringsInSwitch() {
|
||||||
|
return compareTo(JDK1_7) >= 0;
|
||||||
|
}
|
||||||
public boolean allowEncodingErrors() {
|
public boolean allowEncodingErrors() {
|
||||||
return compareTo(JDK1_6) < 0;
|
return compareTo(JDK1_6) < 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,8 @@ public class Attr extends JCTree.Visitor {
|
||||||
allowBoxing = source.allowBoxing();
|
allowBoxing = source.allowBoxing();
|
||||||
allowCovariantReturns = source.allowCovariantReturns();
|
allowCovariantReturns = source.allowCovariantReturns();
|
||||||
allowAnonOuterThis = source.allowAnonOuterThis();
|
allowAnonOuterThis = source.allowAnonOuterThis();
|
||||||
|
allowStringsInSwitch = source.allowStringsInSwitch();
|
||||||
|
sourceName = source.name;
|
||||||
relax = (options.get("-retrofit") != null ||
|
relax = (options.get("-retrofit") != null ||
|
||||||
options.get("-relax") != null);
|
options.get("-relax") != null);
|
||||||
useBeforeDeclarationWarning = options.get("useBeforeDeclarationWarning") != null;
|
useBeforeDeclarationWarning = options.get("useBeforeDeclarationWarning") != null;
|
||||||
|
@ -167,6 +169,16 @@ public class Attr extends JCTree.Visitor {
|
||||||
*/
|
*/
|
||||||
boolean enableSunApiLintControl;
|
boolean enableSunApiLintControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch: allow strings in switch?
|
||||||
|
*/
|
||||||
|
boolean allowStringsInSwitch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch: name of source level; used for error reporting.
|
||||||
|
*/
|
||||||
|
String sourceName;
|
||||||
|
|
||||||
/** Check kind and type of given tree against protokind and prototype.
|
/** Check kind and type of given tree against protokind and prototype.
|
||||||
* If check succeeds, store type in tree and return it.
|
* If check succeeds, store type in tree and return it.
|
||||||
* If check fails, store errType in tree and return it.
|
* If check fails, store errType in tree and return it.
|
||||||
|
@ -886,7 +898,15 @@ public class Attr extends JCTree.Visitor {
|
||||||
boolean enumSwitch =
|
boolean enumSwitch =
|
||||||
allowEnums &&
|
allowEnums &&
|
||||||
(seltype.tsym.flags() & Flags.ENUM) != 0;
|
(seltype.tsym.flags() & Flags.ENUM) != 0;
|
||||||
if (!enumSwitch)
|
boolean stringSwitch = false;
|
||||||
|
if (types.isSameType(seltype, syms.stringType)) {
|
||||||
|
if (allowStringsInSwitch) {
|
||||||
|
stringSwitch = true;
|
||||||
|
} else {
|
||||||
|
log.error(tree.selector.pos(), "string.switch.not.supported.in.source", sourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!enumSwitch && !stringSwitch)
|
||||||
seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType);
|
seltype = chk.checkType(tree.selector.pos(), seltype, syms.intType);
|
||||||
|
|
||||||
// Attribute all cases and
|
// Attribute all cases and
|
||||||
|
@ -909,7 +929,8 @@ public class Attr extends JCTree.Visitor {
|
||||||
Type pattype = attribExpr(c.pat, switchEnv, seltype);
|
Type pattype = attribExpr(c.pat, switchEnv, seltype);
|
||||||
if (pattype.tag != ERROR) {
|
if (pattype.tag != ERROR) {
|
||||||
if (pattype.constValue() == null) {
|
if (pattype.constValue() == null) {
|
||||||
log.error(c.pat.pos(), "const.expr.req");
|
log.error(c.pat.pos(),
|
||||||
|
(stringSwitch ? "string.const.req" : "const.expr.req"));
|
||||||
} else if (labels.contains(pattype.constValue())) {
|
} else if (labels.contains(pattype.constValue())) {
|
||||||
log.error(c.pos(), "duplicate.case.label");
|
log.error(c.pos(), "duplicate.case.label");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -357,7 +357,7 @@ public class Lower extends TreeTranslator {
|
||||||
* case 2: stmt2
|
* case 2: stmt2
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
* with the auxilliary table intialized as follows:
|
* with the auxiliary table initialized as follows:
|
||||||
* <pre>
|
* <pre>
|
||||||
* class Outer$0 {
|
* class Outer$0 {
|
||||||
* synthetic final int[] $EnumMap$Color = new int[Color.values().length];
|
* synthetic final int[] $EnumMap$Color = new int[Color.values().length];
|
||||||
|
@ -858,7 +858,7 @@ public class Lower extends TreeTranslator {
|
||||||
int acode; // The access code of the access method.
|
int acode; // The access code of the access method.
|
||||||
List<Type> argtypes; // The argument types of the access method.
|
List<Type> argtypes; // The argument types of the access method.
|
||||||
Type restype; // The result type of the access method.
|
Type restype; // The result type of the access method.
|
||||||
List<Type> thrown; // The thrown execeptions of the access method.
|
List<Type> thrown; // The thrown exceptions of the access method.
|
||||||
switch (vsym.kind) {
|
switch (vsym.kind) {
|
||||||
case VAR:
|
case VAR:
|
||||||
acode = accessCode(tree, enclOp);
|
acode = accessCode(tree, enclOp);
|
||||||
|
@ -2463,7 +2463,7 @@ public class Lower extends TreeTranslator {
|
||||||
// the dead code, which will not be eliminated during code generation.
|
// the dead code, which will not be eliminated during code generation.
|
||||||
// Note that Flow.isFalse and Flow.isTrue only return true
|
// Note that Flow.isFalse and Flow.isTrue only return true
|
||||||
// for constant expressions in the sense of JLS 15.27, which
|
// for constant expressions in the sense of JLS 15.27, which
|
||||||
// are guaranteed to have no side-effects. More agressive
|
// are guaranteed to have no side-effects. More aggressive
|
||||||
// constant propagation would require that we take care to
|
// constant propagation would require that we take care to
|
||||||
// preserve possible side-effects in the condition expression.
|
// preserve possible side-effects in the condition expression.
|
||||||
|
|
||||||
|
@ -2850,7 +2850,7 @@ public class Lower extends TreeTranslator {
|
||||||
|
|
||||||
// If translated left hand side is an Apply, we are
|
// If translated left hand side is an Apply, we are
|
||||||
// seeing an access method invocation. In this case, return
|
// seeing an access method invocation. In this case, return
|
||||||
// that access method invokation as result.
|
// that access method invocation as result.
|
||||||
if (isUpdateOperator && tree.arg.getTag() == JCTree.APPLY) {
|
if (isUpdateOperator && tree.arg.getTag() == JCTree.APPLY) {
|
||||||
result = tree.arg;
|
result = tree.arg;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2900,7 +2900,7 @@ public class Lower extends TreeTranslator {
|
||||||
}
|
}
|
||||||
// where
|
// where
|
||||||
/**
|
/**
|
||||||
* A statment of the form
|
* A statement of the form
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* for ( T v : arrayexpr ) stmt;
|
* for ( T v : arrayexpr ) stmt;
|
||||||
|
@ -3109,12 +3109,17 @@ public class Lower extends TreeTranslator {
|
||||||
Type selsuper = types.supertype(tree.selector.type);
|
Type selsuper = types.supertype(tree.selector.type);
|
||||||
boolean enumSwitch = selsuper != null &&
|
boolean enumSwitch = selsuper != null &&
|
||||||
(tree.selector.type.tsym.flags() & ENUM) != 0;
|
(tree.selector.type.tsym.flags() & ENUM) != 0;
|
||||||
Type target = enumSwitch ? tree.selector.type : syms.intType;
|
boolean stringSwitch = selsuper != null &&
|
||||||
|
types.isSameType(tree.selector.type, syms.stringType);
|
||||||
|
Type target = enumSwitch ? tree.selector.type :
|
||||||
|
(stringSwitch? syms.stringType : syms.intType);
|
||||||
tree.selector = translate(tree.selector, target);
|
tree.selector = translate(tree.selector, target);
|
||||||
tree.cases = translateCases(tree.cases);
|
tree.cases = translateCases(tree.cases);
|
||||||
if (enumSwitch) {
|
if (enumSwitch) {
|
||||||
result = visitEnumSwitch(tree);
|
result = visitEnumSwitch(tree);
|
||||||
patchTargets(result, tree, result);
|
patchTargets(result, tree, result);
|
||||||
|
} else if (stringSwitch) {
|
||||||
|
result = visitStringSwitch(tree);
|
||||||
} else {
|
} else {
|
||||||
result = tree;
|
result = tree;
|
||||||
}
|
}
|
||||||
|
@ -3144,6 +3149,184 @@ public class Lower extends TreeTranslator {
|
||||||
return make.Switch(selector, cases.toList());
|
return make.Switch(selector, cases.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JCTree visitStringSwitch(JCSwitch tree) {
|
||||||
|
List<JCCase> caseList = tree.getCases();
|
||||||
|
int alternatives = caseList.size();
|
||||||
|
|
||||||
|
if (alternatives == 0) { // Strange but legal possibility
|
||||||
|
return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression()));
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The general approach used is to translate a single
|
||||||
|
* string switch statement into a series of two chained
|
||||||
|
* switch statements: the first a synthesized statement
|
||||||
|
* switching on the argument string's hash value and
|
||||||
|
* computing a string's position in the list of original
|
||||||
|
* case labels, if any, followed by a second switch on the
|
||||||
|
* computed integer value. The second switch has the same
|
||||||
|
* code structure as the original string switch statement
|
||||||
|
* except that the string case labels are replaced with
|
||||||
|
* positional integer constants starting at 0.
|
||||||
|
*
|
||||||
|
* The first switch statement can be thought of as an
|
||||||
|
* inlined map from strings to their position in the case
|
||||||
|
* label list. An alternate implementation would use an
|
||||||
|
* actual Map for this purpose, as done for enum switches.
|
||||||
|
*
|
||||||
|
* With some additional effort, it would be possible to
|
||||||
|
* use a single switch statement on the hash code of the
|
||||||
|
* argument, but care would need to be taken to preserve
|
||||||
|
* the proper control flow in the presence of hash
|
||||||
|
* collisions and other complications, such as
|
||||||
|
* fallthroughs. Switch statements with one or two
|
||||||
|
* alternatives could also be specially translated into
|
||||||
|
* if-then statements to omit the computation of the hash
|
||||||
|
* code.
|
||||||
|
*
|
||||||
|
* The generated code assumes that the hashing algorithm
|
||||||
|
* of String is the same in the compilation environment as
|
||||||
|
* in the environment the code will run in. The string
|
||||||
|
* hashing algorithm in the SE JDK has been unchanged
|
||||||
|
* since at least JDK 1.2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ListBuffer<JCStatement> stmtList = new ListBuffer<JCStatement>();
|
||||||
|
|
||||||
|
// Map from String case labels to their original position in
|
||||||
|
// the list of case labels.
|
||||||
|
Map<String, Integer> caseLabelToPosition =
|
||||||
|
new LinkedHashMap<String, Integer>(alternatives + 1, 1.0f);
|
||||||
|
|
||||||
|
// Map of hash codes to the string case labels having that hashCode.
|
||||||
|
Map<Integer, Set<String>> hashToString =
|
||||||
|
new LinkedHashMap<Integer, Set<String>>(alternatives + 1, 1.0f);
|
||||||
|
|
||||||
|
int casePosition = 0;
|
||||||
|
for(JCCase oneCase : caseList) {
|
||||||
|
JCExpression expression = oneCase.getExpression();
|
||||||
|
|
||||||
|
if (expression != null) { // expression for a "default" case is null
|
||||||
|
String labelExpr = (String) expression.type.constValue();
|
||||||
|
Integer mapping = caseLabelToPosition.put(labelExpr, casePosition);
|
||||||
|
assert mapping == null;
|
||||||
|
int hashCode = labelExpr.hashCode();
|
||||||
|
|
||||||
|
Set<String> stringSet = hashToString.get(hashCode);
|
||||||
|
if (stringSet == null) {
|
||||||
|
stringSet = new LinkedHashSet<String>(1, 1.0f);
|
||||||
|
stringSet.add(labelExpr);
|
||||||
|
hashToString.put(hashCode, stringSet);
|
||||||
|
} else {
|
||||||
|
boolean added = stringSet.add(labelExpr);
|
||||||
|
assert added;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
casePosition++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synthesize a switch statement that has the effect of
|
||||||
|
// mapping from a string to the integer position of that
|
||||||
|
// string in the list of case labels. This is done by
|
||||||
|
// switching on the hashCode of the string followed by an
|
||||||
|
// if-then-else chain comparing the input for equality
|
||||||
|
// with all the case labels having that hash value.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* s$ = top of stack;
|
||||||
|
* tmp$ = -1;
|
||||||
|
* switch($s.hashCode()) {
|
||||||
|
* case caseLabel.hashCode:
|
||||||
|
* if (s$.equals("caseLabel_1")
|
||||||
|
* tmp$ = caseLabelToPosition("caseLabel_1");
|
||||||
|
* else if (s$.equals("caseLabel_2"))
|
||||||
|
* tmp$ = caseLabelToPosition("caseLabel_2");
|
||||||
|
* ...
|
||||||
|
* break;
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
VarSymbol dollar_s = new VarSymbol(FINAL|SYNTHETIC,
|
||||||
|
names.fromString("s" + tree.pos + target.syntheticNameChar()),
|
||||||
|
syms.stringType,
|
||||||
|
currentMethodSym);
|
||||||
|
stmtList.append(make.at(tree.pos()).VarDef(dollar_s, tree.getExpression()).setType(dollar_s.type));
|
||||||
|
|
||||||
|
VarSymbol dollar_tmp = new VarSymbol(SYNTHETIC,
|
||||||
|
names.fromString("tmp" + tree.pos + target.syntheticNameChar()),
|
||||||
|
syms.intType,
|
||||||
|
currentMethodSym);
|
||||||
|
JCVariableDecl dollar_tmp_def =
|
||||||
|
(JCVariableDecl)make.VarDef(dollar_tmp, make.Literal(INT, -1)).setType(dollar_tmp.type);
|
||||||
|
dollar_tmp_def.init.type = dollar_tmp.type = syms.intType;
|
||||||
|
stmtList.append(dollar_tmp_def);
|
||||||
|
ListBuffer<JCCase> caseBuffer = ListBuffer.lb();
|
||||||
|
// hashCode will trigger nullcheck on original switch expression
|
||||||
|
JCMethodInvocation hashCodeCall = makeCall(make.Ident(dollar_s),
|
||||||
|
names.hashCode,
|
||||||
|
List.<JCExpression>nil()).setType(syms.intType);
|
||||||
|
JCSwitch switch1 = make.Switch(hashCodeCall,
|
||||||
|
caseBuffer.toList());
|
||||||
|
for(Map.Entry<Integer, Set<String>> entry : hashToString.entrySet()) {
|
||||||
|
int hashCode = entry.getKey();
|
||||||
|
Set<String> stringsWithHashCode = entry.getValue();
|
||||||
|
assert stringsWithHashCode.size() >= 1;
|
||||||
|
|
||||||
|
JCStatement elsepart = null;
|
||||||
|
for(String caseLabel : stringsWithHashCode ) {
|
||||||
|
JCMethodInvocation stringEqualsCall = makeCall(make.Ident(dollar_s),
|
||||||
|
names.equals,
|
||||||
|
List.<JCExpression>of(make.Literal(caseLabel)));
|
||||||
|
elsepart = make.If(stringEqualsCall,
|
||||||
|
make.Exec(make.Assign(make.Ident(dollar_tmp),
|
||||||
|
make.Literal(caseLabelToPosition.get(caseLabel))).
|
||||||
|
setType(dollar_tmp.type)),
|
||||||
|
elsepart);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListBuffer<JCStatement> lb = ListBuffer.lb();
|
||||||
|
JCBreak breakStmt = make.Break(null);
|
||||||
|
breakStmt.target = switch1;
|
||||||
|
lb.append(elsepart).append(breakStmt);
|
||||||
|
|
||||||
|
caseBuffer.append(make.Case(make.Literal(hashCode), lb.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch1.cases = caseBuffer.toList();
|
||||||
|
stmtList.append(switch1);
|
||||||
|
|
||||||
|
// Make isomorphic switch tree replacing string labels
|
||||||
|
// with corresponding integer ones from the label to
|
||||||
|
// position map.
|
||||||
|
|
||||||
|
ListBuffer<JCCase> lb = ListBuffer.lb();
|
||||||
|
JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList());
|
||||||
|
for(JCCase oneCase : caseList ) {
|
||||||
|
// Rewire up old unlabeled break statements to the
|
||||||
|
// replacement switch being created.
|
||||||
|
patchTargets(oneCase, tree, switch2);
|
||||||
|
|
||||||
|
boolean isDefault = (oneCase.getExpression() == null);
|
||||||
|
JCExpression caseExpr;
|
||||||
|
if (isDefault)
|
||||||
|
caseExpr = null;
|
||||||
|
else {
|
||||||
|
caseExpr = make.Literal(caseLabelToPosition.get((String)oneCase.
|
||||||
|
getExpression().
|
||||||
|
type.constValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
lb.append(make.Case(caseExpr,
|
||||||
|
oneCase.getStatements()));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch2.cases = lb.toList();
|
||||||
|
stmtList.append(switch2);
|
||||||
|
|
||||||
|
return make.Block(0L, stmtList.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void visitNewArray(JCNewArray tree) {
|
public void visitNewArray(JCNewArray tree) {
|
||||||
tree.elemtype = translate(tree.elemtype);
|
tree.elemtype = translate(tree.elemtype);
|
||||||
for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
|
for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)
|
||||||
|
|
|
@ -433,6 +433,8 @@ compiler.err.stack.sim.error=\
|
||||||
Internal error: stack sim error on {0}
|
Internal error: stack sim error on {0}
|
||||||
compiler.err.static.imp.only.classes.and.interfaces=\
|
compiler.err.static.imp.only.classes.and.interfaces=\
|
||||||
static import only from classes and interfaces
|
static import only from classes and interfaces
|
||||||
|
compiler.err.string.const.req=\
|
||||||
|
constant string expression required
|
||||||
compiler.err.synthetic.name.conflict=\
|
compiler.err.synthetic.name.conflict=\
|
||||||
the symbol {0} conflicts with a compiler-synthesized symbol in {1}
|
the symbol {0} conflicts with a compiler-synthesized symbol in {1}
|
||||||
compiler.warn.synthetic.name.conflict=\
|
compiler.warn.synthetic.name.conflict=\
|
||||||
|
@ -1226,6 +1228,10 @@ compiler.err.diamond.not.supported.in.source=\
|
||||||
diamond operator is not supported in -source {0}\n\
|
diamond operator is not supported in -source {0}\n\
|
||||||
(use -source 7 or higher to enable diamond operator)
|
(use -source 7 or higher to enable diamond operator)
|
||||||
|
|
||||||
|
compiler.err.string.switch.not.supported.in.source=\
|
||||||
|
strings in switch are not supported in -source {0}\n\
|
||||||
|
(use -source 7 or higher to enable strings in switch)
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# Diagnostics for where clause implementation
|
# Diagnostics for where clause implementation
|
||||||
# used by the RichDiagnosticFormatter.
|
# used by the RichDiagnosticFormatter.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* @test /nodynamiccopyright/
|
||||||
|
* @bug 6827009
|
||||||
|
* @summary Check for case labels of different types.
|
||||||
|
* @compile/fail -source 6 BadlyTypedLabel1.java
|
||||||
|
* @compile/fail/ref=BadlyTypedLabel1.out -XDstdout -XDrawDiagnostics BadlyTypedLabel1.java
|
||||||
|
*/
|
||||||
|
class BadlyTypedLabel1 {
|
||||||
|
String m(String s) {
|
||||||
|
switch(s) {
|
||||||
|
case "Hello World":
|
||||||
|
return(s);
|
||||||
|
case 42:
|
||||||
|
return ("Don't forget your towel!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
BadlyTypedLabel1.java:13:14: compiler.err.prob.found.req: (compiler.misc.incompatible.types), int, java.lang.String
|
||||||
|
1 error
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* @test /nodynamiccopyright/
|
||||||
|
* @bug 6827009
|
||||||
|
* @summary Check for case lables of different types.
|
||||||
|
* @compile/fail -source 6 BadlyTypedLabel2.java
|
||||||
|
* @compile/fail/ref=BadlyTypedLabel2.out -XDstdout -XDrawDiagnostics BadlyTypedLabel2.java
|
||||||
|
*/
|
||||||
|
import static java.math.RoundingMode.*;
|
||||||
|
|
||||||
|
class BadlyTypedLabel2 {
|
||||||
|
String m(String s) {
|
||||||
|
switch(s) {
|
||||||
|
case "Oh what a feeling...":
|
||||||
|
return(s);
|
||||||
|
case CEILING:
|
||||||
|
return ("... switching on the ceiling!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
BadlyTypedLabel2.java:15:14: compiler.err.prob.found.req: (compiler.misc.incompatible.types), java.math.RoundingMode, java.lang.String
|
||||||
|
1 error
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* @test /nodynamiccopyright/
|
||||||
|
* @bug 6827009
|
||||||
|
* @summary Check for non-constant case labels.
|
||||||
|
* @compile/fail -source 6 NonConstantLabel.java
|
||||||
|
* @compile/fail/ref=NonConstantLabel.out -XDstdout -XDrawDiagnostics NonConstantLabel.java
|
||||||
|
*/
|
||||||
|
class NonConstantLabel {
|
||||||
|
String m(String s) {
|
||||||
|
String fauxConstant = "Goodbye Cruel World";
|
||||||
|
switch(s) {
|
||||||
|
case "Hello World":
|
||||||
|
return(s);
|
||||||
|
case fauxConstant:
|
||||||
|
return (s + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
NonConstantLabel.java:14:14: compiler.err.string.const.req
|
||||||
|
1 error
|
303
langtools/test/tools/javac/StringsInSwitch/OneCaseSwitches.java
Normal file
303
langtools/test/tools/javac/StringsInSwitch/OneCaseSwitches.java
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 6827009
|
||||||
|
* @summary Positive tests for strings in switch with few alternatives.
|
||||||
|
* @compile/fail -source 6 OneCaseSwitches.java
|
||||||
|
* @compile OneCaseSwitches.java
|
||||||
|
* @run main OneCaseSwitches
|
||||||
|
* @author Joseph D. Darcy
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.util.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.*;
|
||||||
|
|
||||||
|
public class OneCaseSwitches {
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@interface TestMeForNull {}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int zeroCasesNoDefault(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 0;
|
||||||
|
switch(s) {
|
||||||
|
}
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int zeroCasesWithDefault(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 2;
|
||||||
|
boolean addResult;
|
||||||
|
|
||||||
|
switch(s) {
|
||||||
|
default:
|
||||||
|
failures = 0;
|
||||||
|
addResult = stringSet.add(s);
|
||||||
|
if (addResult != expected) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("zeroCaseWithDefault: Expectedly got add result of " + addResult +
|
||||||
|
" on string " + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int zeroCasesWithDefaultBreak(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 2;
|
||||||
|
boolean addResult;
|
||||||
|
|
||||||
|
switch(s) {
|
||||||
|
default:
|
||||||
|
failures = zeroCasesWithDefault(s, stringSet, expected);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int oneCaseNoDefault(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 2;
|
||||||
|
boolean addResult;
|
||||||
|
|
||||||
|
switch(s) {
|
||||||
|
case "foo":
|
||||||
|
failures = 0;
|
||||||
|
addResult = stringSet.add(s);
|
||||||
|
if (addResult != expected) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("oneCaseNoDefault: Unexpectedly got add result of " + addResult +
|
||||||
|
" on string " + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int oneCaseNoDefaultBreak(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 2;
|
||||||
|
boolean addResult;
|
||||||
|
|
||||||
|
switch(s) {
|
||||||
|
case "foo":
|
||||||
|
failures = oneCaseNoDefaultBreak(s, stringSet, expected);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int oneCaseWithDefault(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 2;
|
||||||
|
boolean addResult;;
|
||||||
|
|
||||||
|
switch(s) {
|
||||||
|
case "foo":
|
||||||
|
failures = 0;
|
||||||
|
addResult = stringSet.add(s);
|
||||||
|
if (addResult != expected) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("oneCaseNoDefault: Expectedly got add result of " + addResult +
|
||||||
|
" on string " + s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int oneCaseBreakOnly(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 1;
|
||||||
|
switch(s) {
|
||||||
|
case "foo":
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
failures = 0;
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMeForNull
|
||||||
|
public static int oneCaseDefaultBreakOnly(String s, Set<String> stringSet, boolean expected) {
|
||||||
|
int failures = 1;
|
||||||
|
switch(s) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
failures = 0;
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int testNullBehavior() {
|
||||||
|
int failures = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
Method[] methods = OneCaseSwitches.class.getDeclaredMethods();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for(Method method : methods) {
|
||||||
|
count++;
|
||||||
|
try {
|
||||||
|
if (method.isAnnotationPresent(TestMeForNull.class)) {
|
||||||
|
System.out.println("Testing method " + method);
|
||||||
|
method.invoke(null, (String)null, emptyStringSet, false);
|
||||||
|
failures++;
|
||||||
|
System.err.println("Didn't get NPE as expected from " + method);
|
||||||
|
}
|
||||||
|
} catch (InvocationTargetException ite) { // Expected
|
||||||
|
Throwable targetException = ite.getTargetException();
|
||||||
|
if (! (targetException instanceof NullPointerException)) {
|
||||||
|
failures++; // Wrong exception thrown
|
||||||
|
System.err.println("Didn't get expected target exception NPE, got " +
|
||||||
|
ite.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("Did not find any annotated methods.");
|
||||||
|
}
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int testZeroCases() {
|
||||||
|
int failures = 0;
|
||||||
|
Set<String> noDefaultSet = new HashSet<String>();
|
||||||
|
Set<String> defaultSet = new HashSet<String>();
|
||||||
|
|
||||||
|
zeroCasesNoDefault(FOO, noDefaultSet, false);
|
||||||
|
for(String word : words) {
|
||||||
|
zeroCasesNoDefault(word, noDefaultSet, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noDefaultSet.isEmpty()) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("Non-empty set after zeroCasesNoDefault");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String word : words) {
|
||||||
|
zeroCasesWithDefault(word, defaultSet, true);
|
||||||
|
}
|
||||||
|
if (defaultSet.size() != words.length) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("Missing strings after zeroCasesWithDefault");
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int testOneCaseNoDefault() {
|
||||||
|
int failures = 0;
|
||||||
|
Set<String> s = new HashSet<String>();
|
||||||
|
s.add("foo");
|
||||||
|
Set<String> fooSet = Collections.unmodifiableSet(s);
|
||||||
|
Set<String> testSet = new HashSet<String>();
|
||||||
|
|
||||||
|
oneCaseNoDefault(FOO, testSet, true);
|
||||||
|
if (!testSet.equals(fooSet)) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("Unexpected result from oneCaseNoDefault: didn't get {\"Foo\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String word : words) {
|
||||||
|
oneCaseNoDefault(word, testSet, false);
|
||||||
|
}
|
||||||
|
if (!testSet.equals(fooSet)) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("Unexpected result from oneCaseNoDefault: didn't get {\"Foo\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int testBreakOnly() {
|
||||||
|
int failures = 0;
|
||||||
|
|
||||||
|
for(String word : words) {
|
||||||
|
failures += oneCaseBreakOnly(word, emptyStringSet, true);
|
||||||
|
failures += oneCaseDefaultBreakOnly(word, emptyStringSet, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int testExpressionEval() {
|
||||||
|
String s = "a";
|
||||||
|
int errors = 2;
|
||||||
|
|
||||||
|
System.out.println("Testing expression evaluation.");
|
||||||
|
|
||||||
|
switch (s + s) {
|
||||||
|
case "aa":
|
||||||
|
errors = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "aaaa":
|
||||||
|
errors = 1;
|
||||||
|
System.err.println("Suspected bad expression evaluation.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Should not reach here.");
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String FOO = "foo";
|
||||||
|
|
||||||
|
static final String[] words = {"baz",
|
||||||
|
"quux",
|
||||||
|
"wombat",
|
||||||
|
"\u0ccc\u0012"}; // hash collision with "foo"
|
||||||
|
|
||||||
|
final static Set<String> emptyStringSet = Collections.emptySet();
|
||||||
|
|
||||||
|
public static void main(String... args) {
|
||||||
|
int failures = 0;
|
||||||
|
|
||||||
|
failures += testNullBehavior();
|
||||||
|
failures += testZeroCases();
|
||||||
|
failures += testOneCaseNoDefault();
|
||||||
|
failures += testBreakOnly();
|
||||||
|
failures += testExpressionEval();
|
||||||
|
|
||||||
|
if (failures > 0) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
langtools/test/tools/javac/StringsInSwitch/RSCL1.out
Normal file
2
langtools/test/tools/javac/StringsInSwitch/RSCL1.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
RepeatedStringCaseLabels1.java:13:9: compiler.err.duplicate.case.label
|
||||||
|
1 error
|
2
langtools/test/tools/javac/StringsInSwitch/RSCL2.out
Normal file
2
langtools/test/tools/javac/StringsInSwitch/RSCL2.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
RepeatedStringCaseLabels2.java:14:9: compiler.err.duplicate.case.label
|
||||||
|
1 error
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* @test /nodynamiccopyright/
|
||||||
|
* @bug 6827009
|
||||||
|
* @summary Check for repeated string case labels.
|
||||||
|
* @compile/fail -source 6 RepeatedStringCaseLabels1.java
|
||||||
|
* @compile/fail/ref=RSCL1.out -XDstdout -XDrawDiagnostics RepeatedStringCaseLabels1.java
|
||||||
|
*/
|
||||||
|
class RepeatedStringCaseLabels1 {
|
||||||
|
String m(String s) {
|
||||||
|
switch(s) {
|
||||||
|
case "Hello World":
|
||||||
|
return(s);
|
||||||
|
case "Hello" + " " + "World":
|
||||||
|
return (s + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* @test /nodynamiccopyright/
|
||||||
|
* @bug 6827009
|
||||||
|
* @summary Check for repeated string case labels.
|
||||||
|
* @compile/fail -source 6 RepeatedStringCaseLabels2.java
|
||||||
|
* @compile/fail/ref=RSCL2.out -XDstdout -XDrawDiagnostics RepeatedStringCaseLabels2.java
|
||||||
|
*/
|
||||||
|
class RepeatedStringCaseLabels2 {
|
||||||
|
String m(String s) {
|
||||||
|
final String constant = "Hello" + " " + "World";
|
||||||
|
switch(s) {
|
||||||
|
case "Hello World":
|
||||||
|
return(s);
|
||||||
|
case constant:
|
||||||
|
return (s + s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
263
langtools/test/tools/javac/StringsInSwitch/StringSwitches.java
Normal file
263
langtools/test/tools/javac/StringsInSwitch/StringSwitches.java
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 6827009
|
||||||
|
* @summary Positive tests for strings in switch.
|
||||||
|
* @author Joseph D. Darcy
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class StringSwitches {
|
||||||
|
|
||||||
|
public static void main(String... args) {
|
||||||
|
int failures = 0;
|
||||||
|
|
||||||
|
failures += testPileup();
|
||||||
|
failures += testSwitchingTwoWays();
|
||||||
|
failures += testNamedBreak();
|
||||||
|
|
||||||
|
if (failures > 0) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A zero length string and all strings consisting only of the
|
||||||
|
* zero character \u0000 have a hash code of zero. This method
|
||||||
|
* maps such strings to the number of times \u0000 appears for 0
|
||||||
|
* through 6 occurrences.
|
||||||
|
*/
|
||||||
|
private static int zeroHashes(String s) {
|
||||||
|
int result = Integer.MAX_VALUE;
|
||||||
|
switch(s) {
|
||||||
|
case "":
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case "\u0000":
|
||||||
|
result = 1; break;
|
||||||
|
|
||||||
|
case "\u0000\u0000":
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case "\u0000\u0000\u0000":
|
||||||
|
result = 3; break;
|
||||||
|
|
||||||
|
case "\u0000\u0000\u0000\u0000":
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case "\u0000\u0000\u0000\u0000\u0000":
|
||||||
|
result = 5; break;
|
||||||
|
|
||||||
|
case "\u0000\u0000\u0000\u0000\u0000\u0000":
|
||||||
|
return 6;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int testPileup() {
|
||||||
|
int failures = 0;
|
||||||
|
String zero = "";
|
||||||
|
for(int i = 0; i <= 6; i++, zero += "\u0000") {
|
||||||
|
int result = zeroHashes(zero);
|
||||||
|
if (result != i) {
|
||||||
|
failures++;
|
||||||
|
System.err.printf("For string \"%s\" unexpectedly got %d instead of %d%n.",
|
||||||
|
zero, result, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zeroHashes("foo") != -1) {
|
||||||
|
failures++;
|
||||||
|
System.err.println("Failed to get -1 for input string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that a switch on an enum and a switch with the same
|
||||||
|
* structure on the string name of an enum compute equivalent
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
|
private static int testSwitchingTwoWays() {
|
||||||
|
int failures = 0;
|
||||||
|
|
||||||
|
for(MetaSynVar msv : MetaSynVar.values()) {
|
||||||
|
int enumResult = enumSwitch(msv);
|
||||||
|
int stringResult = stringSwitch(msv.name());
|
||||||
|
|
||||||
|
if (enumResult != stringResult) {
|
||||||
|
failures++;
|
||||||
|
System.err.printf("One value %s, computed 0x%x with the enum switch " +
|
||||||
|
"and 0x%x with the string one.%n",
|
||||||
|
msv, enumResult, stringResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static enum MetaSynVar {
|
||||||
|
FOO,
|
||||||
|
BAR,
|
||||||
|
BAZ,
|
||||||
|
QUX,
|
||||||
|
QUUX,
|
||||||
|
QUUUX,
|
||||||
|
MUMBLE,
|
||||||
|
FOOBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int enumSwitch(MetaSynVar msv) {
|
||||||
|
int result = 0;
|
||||||
|
switch(msv) {
|
||||||
|
case FOO:
|
||||||
|
result |= (1<<0);
|
||||||
|
// fallthrough:
|
||||||
|
|
||||||
|
case BAR:
|
||||||
|
case BAZ:
|
||||||
|
result |= (1<<1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch(msv) {
|
||||||
|
case QUX:
|
||||||
|
result |= (1<<2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QUUX:
|
||||||
|
result |= (1<<3);
|
||||||
|
|
||||||
|
default:
|
||||||
|
result |= (1<<4);
|
||||||
|
}
|
||||||
|
result |= (1<<5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MUMBLE:
|
||||||
|
result |= (1<<6);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
case FOOBAR:
|
||||||
|
result |= (1<<7);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result |= (1<<8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int stringSwitch(String msvName) {
|
||||||
|
int result = 0;
|
||||||
|
switch(msvName) {
|
||||||
|
case "FOO":
|
||||||
|
result |= (1<<0);
|
||||||
|
// fallthrough:
|
||||||
|
|
||||||
|
case "BAR":
|
||||||
|
case "BAZ":
|
||||||
|
result |= (1<<1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch(msvName) {
|
||||||
|
case "QUX":
|
||||||
|
result |= (1<<2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "QUUX":
|
||||||
|
result |= (1<<3);
|
||||||
|
|
||||||
|
default:
|
||||||
|
result |= (1<<4);
|
||||||
|
}
|
||||||
|
result |= (1<<5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "MUMBLE":
|
||||||
|
result |= (1<<6);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
case "FOOBAR":
|
||||||
|
result |= (1<<7);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result |= (1<<8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int testNamedBreak() {
|
||||||
|
int failures = 0;
|
||||||
|
String[] testStrings = {"a", "b", "c", "d", "e"};
|
||||||
|
int[] testExpected = { 0b101011, 0b101, 0b100001, 0b101000, 0b10000};
|
||||||
|
|
||||||
|
for(int i = 0; i < testStrings.length; i++) {
|
||||||
|
int expected = testExpected[i];
|
||||||
|
int result = namedBreak(testStrings[i]);
|
||||||
|
|
||||||
|
if (result != expected) {
|
||||||
|
failures++;
|
||||||
|
|
||||||
|
System.err.printf("On input %s, got %d instead of %d.%n",
|
||||||
|
testStrings[i], result, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int namedBreak(String s) {
|
||||||
|
int result = 0;
|
||||||
|
outer: switch(s) {
|
||||||
|
case "a":
|
||||||
|
case "b":
|
||||||
|
case "c":
|
||||||
|
result |= (1<<0);
|
||||||
|
inner: switch(s + s) {
|
||||||
|
case "aa":
|
||||||
|
result |= (1<<1);
|
||||||
|
break inner;
|
||||||
|
|
||||||
|
case "cc":
|
||||||
|
break outer;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result |= (1<<2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "d":
|
||||||
|
result |= (1<<3);
|
||||||
|
break outer;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return result |= (1<<4);
|
||||||
|
}
|
||||||
|
result |= (1<<5);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue