1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Lower.java Fri Oct 30 10:55:00 2009 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Lower.java Mon Nov 02 21:36:59 2009 -0800 1.3 @@ -357,7 +357,7 @@ 1.4 * case 2: stmt2 1.5 * } 1.6 * </pre> 1.7 - * with the auxilliary table intialized as follows: 1.8 + * with the auxiliary table initialized as follows: 1.9 * <pre> 1.10 * class Outer$0 { 1.11 * synthetic final int[] $EnumMap$Color = new int[Color.values().length]; 1.12 @@ -858,7 +858,7 @@ 1.13 int acode; // The access code of the access method. 1.14 List<Type> argtypes; // The argument types of the access method. 1.15 Type restype; // The result type of the access method. 1.16 - List<Type> thrown; // The thrown execeptions of the access method. 1.17 + List<Type> thrown; // The thrown exceptions of the access method. 1.18 switch (vsym.kind) { 1.19 case VAR: 1.20 acode = accessCode(tree, enclOp); 1.21 @@ -2463,7 +2463,7 @@ 1.22 // the dead code, which will not be eliminated during code generation. 1.23 // Note that Flow.isFalse and Flow.isTrue only return true 1.24 // for constant expressions in the sense of JLS 15.27, which 1.25 - // are guaranteed to have no side-effects. More agressive 1.26 + // are guaranteed to have no side-effects. More aggressive 1.27 // constant propagation would require that we take care to 1.28 // preserve possible side-effects in the condition expression. 1.29 1.30 @@ -2850,7 +2850,7 @@ 1.31 1.32 // If translated left hand side is an Apply, we are 1.33 // seeing an access method invocation. In this case, return 1.34 - // that access method invokation as result. 1.35 + // that access method invocation as result. 1.36 if (isUpdateOperator && tree.arg.getTag() == JCTree.APPLY) { 1.37 result = tree.arg; 1.38 } else { 1.39 @@ -2900,7 +2900,7 @@ 1.40 } 1.41 // where 1.42 /** 1.43 - * A statment of the form 1.44 + * A statement of the form 1.45 * 1.46 * <pre> 1.47 * for ( T v : arrayexpr ) stmt; 1.48 @@ -3109,12 +3109,17 @@ 1.49 Type selsuper = types.supertype(tree.selector.type); 1.50 boolean enumSwitch = selsuper != null && 1.51 (tree.selector.type.tsym.flags() & ENUM) != 0; 1.52 - Type target = enumSwitch ? tree.selector.type : syms.intType; 1.53 + boolean stringSwitch = selsuper != null && 1.54 + types.isSameType(tree.selector.type, syms.stringType); 1.55 + Type target = enumSwitch ? tree.selector.type : 1.56 + (stringSwitch? syms.stringType : syms.intType); 1.57 tree.selector = translate(tree.selector, target); 1.58 tree.cases = translateCases(tree.cases); 1.59 if (enumSwitch) { 1.60 result = visitEnumSwitch(tree); 1.61 patchTargets(result, tree, result); 1.62 + } else if (stringSwitch) { 1.63 + result = visitStringSwitch(tree); 1.64 } else { 1.65 result = tree; 1.66 } 1.67 @@ -3144,6 +3149,184 @@ 1.68 return make.Switch(selector, cases.toList()); 1.69 } 1.70 1.71 + public JCTree visitStringSwitch(JCSwitch tree) { 1.72 + List<JCCase> caseList = tree.getCases(); 1.73 + int alternatives = caseList.size(); 1.74 + 1.75 + if (alternatives == 0) { // Strange but legal possibility 1.76 + return make.at(tree.pos()).Exec(attr.makeNullCheck(tree.getExpression())); 1.77 + } else { 1.78 + /* 1.79 + * The general approach used is to translate a single 1.80 + * string switch statement into a series of two chained 1.81 + * switch statements: the first a synthesized statement 1.82 + * switching on the argument string's hash value and 1.83 + * computing a string's position in the list of original 1.84 + * case labels, if any, followed by a second switch on the 1.85 + * computed integer value. The second switch has the same 1.86 + * code structure as the original string switch statement 1.87 + * except that the string case labels are replaced with 1.88 + * positional integer constants starting at 0. 1.89 + * 1.90 + * The first switch statement can be thought of as an 1.91 + * inlined map from strings to their position in the case 1.92 + * label list. An alternate implementation would use an 1.93 + * actual Map for this purpose, as done for enum switches. 1.94 + * 1.95 + * With some additional effort, it would be possible to 1.96 + * use a single switch statement on the hash code of the 1.97 + * argument, but care would need to be taken to preserve 1.98 + * the proper control flow in the presence of hash 1.99 + * collisions and other complications, such as 1.100 + * fallthroughs. Switch statements with one or two 1.101 + * alternatives could also be specially translated into 1.102 + * if-then statements to omit the computation of the hash 1.103 + * code. 1.104 + * 1.105 + * The generated code assumes that the hashing algorithm 1.106 + * of String is the same in the compilation environment as 1.107 + * in the environment the code will run in. The string 1.108 + * hashing algorithm in the SE JDK has been unchanged 1.109 + * since at least JDK 1.2. 1.110 + */ 1.111 + 1.112 + ListBuffer<JCStatement> stmtList = new ListBuffer<JCStatement>(); 1.113 + 1.114 + // Map from String case labels to their original position in 1.115 + // the list of case labels. 1.116 + Map<String, Integer> caseLabelToPosition = 1.117 + new LinkedHashMap<String, Integer>(alternatives + 1, 1.0f); 1.118 + 1.119 + // Map of hash codes to the string case labels having that hashCode. 1.120 + Map<Integer, Set<String>> hashToString = 1.121 + new LinkedHashMap<Integer, Set<String>>(alternatives + 1, 1.0f); 1.122 + 1.123 + int casePosition = 0; 1.124 + for(JCCase oneCase : caseList) { 1.125 + JCExpression expression = oneCase.getExpression(); 1.126 + 1.127 + if (expression != null) { // expression for a "default" case is null 1.128 + String labelExpr = (String) expression.type.constValue(); 1.129 + Integer mapping = caseLabelToPosition.put(labelExpr, casePosition); 1.130 + assert mapping == null; 1.131 + int hashCode = labelExpr.hashCode(); 1.132 + 1.133 + Set<String> stringSet = hashToString.get(hashCode); 1.134 + if (stringSet == null) { 1.135 + stringSet = new LinkedHashSet<String>(1, 1.0f); 1.136 + stringSet.add(labelExpr); 1.137 + hashToString.put(hashCode, stringSet); 1.138 + } else { 1.139 + boolean added = stringSet.add(labelExpr); 1.140 + assert added; 1.141 + } 1.142 + } 1.143 + casePosition++; 1.144 + } 1.145 + 1.146 + // Synthesize a switch statement that has the effect of 1.147 + // mapping from a string to the integer position of that 1.148 + // string in the list of case labels. This is done by 1.149 + // switching on the hashCode of the string followed by an 1.150 + // if-then-else chain comparing the input for equality 1.151 + // with all the case labels having that hash value. 1.152 + 1.153 + /* 1.154 + * s$ = top of stack; 1.155 + * tmp$ = -1; 1.156 + * switch($s.hashCode()) { 1.157 + * case caseLabel.hashCode: 1.158 + * if (s$.equals("caseLabel_1") 1.159 + * tmp$ = caseLabelToPosition("caseLabel_1"); 1.160 + * else if (s$.equals("caseLabel_2")) 1.161 + * tmp$ = caseLabelToPosition("caseLabel_2"); 1.162 + * ... 1.163 + * break; 1.164 + * ... 1.165 + * } 1.166 + */ 1.167 + 1.168 + VarSymbol dollar_s = new VarSymbol(FINAL|SYNTHETIC, 1.169 + names.fromString("s" + tree.pos + target.syntheticNameChar()), 1.170 + syms.stringType, 1.171 + currentMethodSym); 1.172 + stmtList.append(make.at(tree.pos()).VarDef(dollar_s, tree.getExpression()).setType(dollar_s.type)); 1.173 + 1.174 + VarSymbol dollar_tmp = new VarSymbol(SYNTHETIC, 1.175 + names.fromString("tmp" + tree.pos + target.syntheticNameChar()), 1.176 + syms.intType, 1.177 + currentMethodSym); 1.178 + JCVariableDecl dollar_tmp_def = 1.179 + (JCVariableDecl)make.VarDef(dollar_tmp, make.Literal(INT, -1)).setType(dollar_tmp.type); 1.180 + dollar_tmp_def.init.type = dollar_tmp.type = syms.intType; 1.181 + stmtList.append(dollar_tmp_def); 1.182 + ListBuffer<JCCase> caseBuffer = ListBuffer.lb(); 1.183 + // hashCode will trigger nullcheck on original switch expression 1.184 + JCMethodInvocation hashCodeCall = makeCall(make.Ident(dollar_s), 1.185 + names.hashCode, 1.186 + List.<JCExpression>nil()).setType(syms.intType); 1.187 + JCSwitch switch1 = make.Switch(hashCodeCall, 1.188 + caseBuffer.toList()); 1.189 + for(Map.Entry<Integer, Set<String>> entry : hashToString.entrySet()) { 1.190 + int hashCode = entry.getKey(); 1.191 + Set<String> stringsWithHashCode = entry.getValue(); 1.192 + assert stringsWithHashCode.size() >= 1; 1.193 + 1.194 + JCStatement elsepart = null; 1.195 + for(String caseLabel : stringsWithHashCode ) { 1.196 + JCMethodInvocation stringEqualsCall = makeCall(make.Ident(dollar_s), 1.197 + names.equals, 1.198 + List.<JCExpression>of(make.Literal(caseLabel))); 1.199 + elsepart = make.If(stringEqualsCall, 1.200 + make.Exec(make.Assign(make.Ident(dollar_tmp), 1.201 + make.Literal(caseLabelToPosition.get(caseLabel))). 1.202 + setType(dollar_tmp.type)), 1.203 + elsepart); 1.204 + } 1.205 + 1.206 + ListBuffer<JCStatement> lb = ListBuffer.lb(); 1.207 + JCBreak breakStmt = make.Break(null); 1.208 + breakStmt.target = switch1; 1.209 + lb.append(elsepart).append(breakStmt); 1.210 + 1.211 + caseBuffer.append(make.Case(make.Literal(hashCode), lb.toList())); 1.212 + } 1.213 + 1.214 + switch1.cases = caseBuffer.toList(); 1.215 + stmtList.append(switch1); 1.216 + 1.217 + // Make isomorphic switch tree replacing string labels 1.218 + // with corresponding integer ones from the label to 1.219 + // position map. 1.220 + 1.221 + ListBuffer<JCCase> lb = ListBuffer.lb(); 1.222 + JCSwitch switch2 = make.Switch(make.Ident(dollar_tmp), lb.toList()); 1.223 + for(JCCase oneCase : caseList ) { 1.224 + // Rewire up old unlabeled break statements to the 1.225 + // replacement switch being created. 1.226 + patchTargets(oneCase, tree, switch2); 1.227 + 1.228 + boolean isDefault = (oneCase.getExpression() == null); 1.229 + JCExpression caseExpr; 1.230 + if (isDefault) 1.231 + caseExpr = null; 1.232 + else { 1.233 + caseExpr = make.Literal(caseLabelToPosition.get((String)oneCase. 1.234 + getExpression(). 1.235 + type.constValue())); 1.236 + } 1.237 + 1.238 + lb.append(make.Case(caseExpr, 1.239 + oneCase.getStatements())); 1.240 + } 1.241 + 1.242 + switch2.cases = lb.toList(); 1.243 + stmtList.append(switch2); 1.244 + 1.245 + return make.Block(0L, stmtList.toList()); 1.246 + } 1.247 + } 1.248 + 1.249 public void visitNewArray(JCNewArray tree) { 1.250 tree.elemtype = translate(tree.elemtype); 1.251 for (List<JCExpression> t = tree.dims; t.tail != null; t = t.tail)