提交 b9f5bce4 编写于 作者: D darcy

6827009: Project Coin: Strings in Switch

Reviewed-by: jjg, mcimadamore
上级 c007cb57
......@@ -110,6 +110,9 @@ public enum Source {
}
/** Allow encoding errors, giving only warnings. */
public boolean allowStringsInSwitch() {
return compareTo(JDK1_7) >= 0;
}
public boolean allowEncodingErrors() {
return compareTo(JDK1_6) < 0;
}
......
......@@ -115,6 +115,8 @@ public class Attr extends JCTree.Visitor {
allowBoxing = source.allowBoxing();
allowCovariantReturns = source.allowCovariantReturns();
allowAnonOuterThis = source.allowAnonOuterThis();
allowStringsInSwitch = source.allowStringsInSwitch();
sourceName = source.name;
relax = (options.get("-retrofit") != null ||
options.get("-relax") != null);
useBeforeDeclarationWarning = options.get("useBeforeDeclarationWarning") != null;
......@@ -167,6 +169,16 @@ public class Attr extends JCTree.Visitor {
*/
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.
* If check succeeds, store type 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 =
allowEnums &&
(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);
// Attribute all cases and
......@@ -909,7 +929,8 @@ public class Attr extends JCTree.Visitor {
Type pattype = attribExpr(c.pat, switchEnv, seltype);
if (pattype.tag != ERROR) {
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())) {
log.error(c.pos(), "duplicate.case.label");
} else {
......
......@@ -357,7 +357,7 @@ public class Lower extends TreeTranslator {
* case 2: stmt2
* }
* </pre>
* with the auxilliary table intialized as follows:
* with the auxiliary table initialized as follows:
* <pre>
* class Outer$0 {
* 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.
List<Type> argtypes; // The argument types 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) {
case VAR:
acode = accessCode(tree, enclOp);
......@@ -2463,7 +2463,7 @@ public class Lower extends TreeTranslator {
// the dead code, which will not be eliminated during code generation.
// Note that Flow.isFalse and Flow.isTrue only return true
// 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
// 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
// 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) {
result = tree.arg;
} else {
......@@ -2900,7 +2900,7 @@ public class Lower extends TreeTranslator {
}
// where
/**
* A statment of the form
* A statement of the form
*
* <pre>
* for ( T v : arrayexpr ) stmt;
......@@ -3109,12 +3109,17 @@ public class Lower extends TreeTranslator {
Type selsuper = types.supertype(tree.selector.type);
boolean enumSwitch = selsuper != null &&
(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.cases = translateCases(tree.cases);
if (enumSwitch) {
result = visitEnumSwitch(tree);
patchTargets(result, tree, result);
} else if (stringSwitch) {
result = visitStringSwitch(tree);
} else {
result = tree;
}
......@@ -3144,6 +3149,184 @@ public class Lower extends TreeTranslator {
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) {
tree.elemtype = translate(tree.elemtype);
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}
compiler.err.static.imp.only.classes.and.interfaces=\
static import only from classes and interfaces
compiler.err.string.const.req=\
constant string expression required
compiler.err.synthetic.name.conflict=\
the symbol {0} conflicts with a compiler-synthesized symbol in {1}
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\
(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
# used by the RichDiagnosticFormatter.
......
/*
* @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!");
}
}
}
BadlyTypedLabel1.java:13:14: compiler.err.prob.found.req: (compiler.misc.incompatible.types), int, java.lang.String
1 error
/*
* @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!");
}
}
}
BadlyTypedLabel2.java:15:14: compiler.err.prob.found.req: (compiler.misc.incompatible.types), java.math.RoundingMode, java.lang.String
1 error
/*
* @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);
}
}
}
NonConstantLabel.java:14:14: compiler.err.string.const.req
1 error
/*
* 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();
}
}
}
RepeatedStringCaseLabels1.java:13:9: compiler.err.duplicate.case.label
1 error
RepeatedStringCaseLabels2.java:14:9: compiler.err.duplicate.case.label
1 error
/*
* @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);
}
}
}
/*
* @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);
}
}
}
/*
* 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;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册