src/share/classes/com/sun/tools/javac/comp/Lower.java

changeset 430
8fb9b4be3cb1
parent 359
8227961c64d3
child 443
121e0ebf1658
     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)

mercurial