7120463: Fix method reference parser support in order to avoid ambiguities

Mon, 19 Dec 2011 12:07:07 +0000

author
mcimadamore
date
Mon, 19 Dec 2011 12:07:07 +0000
changeset 1165
1ae5988e201b
parent 1164
a7a2720c7897
child 1166
77b2c066084c
child 1169
116f68a5e677

7120463: Fix method reference parser support in order to avoid ambiguities
Summary: Add lookahead routine to disambiguate between method reference in method context and binary expression
Reviewed-by: jjg, dlsmith

src/share/classes/com/sun/tools/javac/parser/JavacParser.java file | annotate | diff | comparison | revisions
test/tools/javac/lambda/MethodReferenceParserTest.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Dec 16 16:41:00 2011 -0800
     1.2 +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Mon Dec 19 12:07:07 2011 +0000
     1.3 @@ -787,7 +787,7 @@
     1.4              top++;
     1.5              topOp = token;
     1.6              nextToken();
     1.7 -            odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3NoParams();
     1.8 +            odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3();
     1.9              while (top > 0 && prec(topOp.kind) >= prec(token.kind)) {
    1.10                  odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1],
    1.11                                          odStack[top]);
    1.12 @@ -931,7 +931,7 @@
    1.13                      mode = EXPR;
    1.14                      t = literal(names.hyphen, pos);
    1.15                  } else {
    1.16 -                    t = term3NoParams();
    1.17 +                    t = term3();
    1.18                      return F.at(pos).Unary(unoptag(tk), t);
    1.19                  }
    1.20              } else return illegal();
    1.21 @@ -947,8 +947,8 @@
    1.22                      break;
    1.23                  } else {
    1.24                      nextToken();
    1.25 -                    mode = EXPR | TYPE;
    1.26 -                    t = term3NoParams();
    1.27 +                    mode = EXPR | TYPE | NOPARAMS;
    1.28 +                    t = term3();
    1.29                      if ((mode & TYPE) != 0 && token.kind == LT) {
    1.30                          // Could be a cast to a parameterized type
    1.31                          JCTree.Tag op = JCTree.Tag.LT;
    1.32 @@ -1011,7 +1011,7 @@
    1.33                  lastmode = mode;
    1.34                  mode = EXPR;
    1.35                  if ((lastmode & EXPR) == 0) {
    1.36 -                    JCExpression t1 = term3NoParams();
    1.37 +                    JCExpression t1 = term3();
    1.38                      return F.at(pos).TypeCast(t, t1);
    1.39                  } else if ((lastmode & TYPE) != 0) {
    1.40                      switch (token.kind) {
    1.41 @@ -1024,7 +1024,7 @@
    1.42                          case NEW: case IDENTIFIER: case ASSERT: case ENUM:
    1.43                      case BYTE: case SHORT: case CHAR: case INT:
    1.44                      case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
    1.45 -                        JCExpression t1 = term3NoParams();
    1.46 +                        JCExpression t1 = term3();
    1.47                          return F.at(pos).TypeCast(t, t1);
    1.48                      }
    1.49                  }
    1.50 @@ -1143,49 +1143,35 @@
    1.51                          // typeArgs saved for next loop iteration.
    1.52                          t = toP(F.at(pos).Select(t, ident()));
    1.53                          break;
    1.54 -//                    case LT:
    1.55 -//                        if ((mode & (TYPE | NOPARAMS)) == 0) {
    1.56 -//                            //could be an unbound method reference whose qualifier
    1.57 -//                            //is a generic type i.e. A<S>#m
    1.58 -//                            mode = EXPR | TYPE;
    1.59 -//                            JCTree.Tag op = JCTree.Tag.LT;
    1.60 -//                            int pos1 = token.pos;
    1.61 -//                            nextToken();
    1.62 -//                            mode |= EXPR | TYPE | TYPEARG;
    1.63 -//                            JCExpression t1 = term3();
    1.64 -//                            if ((mode & TYPE) != 0 &&
    1.65 -//                                (token.kind == COMMA || token.kind == GT)) {
    1.66 -//                                mode = TYPE;
    1.67 -//                                ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
    1.68 -//                                args.append(t1);
    1.69 -//                                while (token.kind == COMMA) {
    1.70 -//                                    nextToken();
    1.71 -//                                    args.append(typeArgument());
    1.72 -//                                }
    1.73 -//                                accept(GT);
    1.74 -//                                t = toP(F.at(pos1).TypeApply(t, args.toList()));
    1.75 -//                                checkGenerics();
    1.76 -//                                while (token.kind == DOT) {
    1.77 -//                                    nextToken();
    1.78 -//                                    mode = TYPE;
    1.79 -//                                    t = toP(F.at(token.pos).Select(t, ident()));
    1.80 -//                                    t = typeArgumentsOpt(t);
    1.81 -//                                }
    1.82 -//                                if (token.kind != HASH) {
    1.83 -//                                    //method reference expected here
    1.84 -//                                    t = illegal();
    1.85 -//                                }
    1.86 -//                                mode = EXPR;
    1.87 -//                                break;
    1.88 -//                            } else if ((mode & EXPR) != 0) {
    1.89 -//                                //rollback - it was a binary expression
    1.90 -//                                mode = EXPR;
    1.91 -//                                JCExpression e = term2Rest(t1, TreeInfo.shiftPrec);
    1.92 -//                                t = F.at(pos1).Binary(op, t, e);
    1.93 -//                                t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec)));
    1.94 -//                            }
    1.95 -//                        }
    1.96 -//                        break loop;
    1.97 +                    case LT:
    1.98 +                        if ((mode & TYPE) == 0 && isUnboundMemberRef()) {
    1.99 +                            //this is an unbound method reference whose qualifier
   1.100 +                            //is a generic type i.e. A<S>#m
   1.101 +                            int pos1 = token.pos;
   1.102 +                            accept(LT);
   1.103 +                            ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
   1.104 +                            args.append(typeArgument());
   1.105 +                            while (token.kind == COMMA) {
   1.106 +                                nextToken();
   1.107 +                                args.append(typeArgument());
   1.108 +                            }
   1.109 +                            accept(GT);
   1.110 +                            t = toP(F.at(pos1).TypeApply(t, args.toList()));
   1.111 +                            checkGenerics();
   1.112 +                            while (token.kind == DOT) {
   1.113 +                                nextToken();
   1.114 +                                mode = TYPE;
   1.115 +                                t = toP(F.at(token.pos).Select(t, ident()));
   1.116 +                                t = typeArgumentsOpt(t);
   1.117 +                            }
   1.118 +                            if (token.kind != HASH) {
   1.119 +                                //method reference expected here
   1.120 +                                t = illegal();
   1.121 +                            }
   1.122 +                            mode = EXPR;
   1.123 +                            return term3Rest(t, typeArgs);
   1.124 +                        }
   1.125 +                        break loop;
   1.126                      default:
   1.127                          break loop;
   1.128                      }
   1.129 @@ -1225,15 +1211,6 @@
   1.130          return term3Rest(t, typeArgs);
   1.131      }
   1.132  
   1.133 -    JCExpression term3NoParams() {
   1.134 -        try {
   1.135 -            mode |= NOPARAMS;
   1.136 -            return term3();
   1.137 -        } finally {
   1.138 -            mode &= ~NOPARAMS;
   1.139 -        }
   1.140 -    }
   1.141 -
   1.142      JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) {
   1.143          if (typeArgs != null) illegal();
   1.144          while (true) {
   1.145 @@ -1297,6 +1274,41 @@
   1.146          return toP(t);
   1.147      }
   1.148  
   1.149 +    /**
   1.150 +     * If we see an identifier followed by a '&lt;' it could be an unbound
   1.151 +     * method reference or a binary expression. To disambiguate, look for a
   1.152 +     * matching '&gt;' and see if the subsequent terminal is either '.' or '#'.
   1.153 +     */
   1.154 +    @SuppressWarnings("fallthrough")
   1.155 +    boolean isUnboundMemberRef() {
   1.156 +        int pos = 0, depth = 0;
   1.157 +        for (Token t = S.token(pos) ; ; t = S.token(++pos)) {
   1.158 +            switch (t.kind) {
   1.159 +                case IDENTIFIER: case QUES: case EXTENDS: case SUPER:
   1.160 +                case DOT: case RBRACKET: case LBRACKET: case COMMA:
   1.161 +                case BYTE: case SHORT: case INT: case LONG: case FLOAT:
   1.162 +                case DOUBLE: case BOOLEAN: case CHAR:
   1.163 +                    break;
   1.164 +                case LT:
   1.165 +                    depth++; break;
   1.166 +                case GTGTGT:
   1.167 +                    depth--;
   1.168 +                case GTGT:
   1.169 +                    depth--;
   1.170 +                case GT:
   1.171 +                    depth--;
   1.172 +                    if (depth == 0) {
   1.173 +                        return
   1.174 +                            S.token(pos + 1).kind == TokenKind.DOT ||
   1.175 +                            S.token(pos + 1).kind == TokenKind.HASH;
   1.176 +                    }
   1.177 +                    break;
   1.178 +                default:
   1.179 +                    return false;
   1.180 +            }
   1.181 +        }
   1.182 +    }
   1.183 +
   1.184      JCExpression lambdaExpressionOrStatement(JCVariableDecl firstParam, int pos) {
   1.185          ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>();
   1.186          params.append(firstParam);
     2.1 --- a/test/tools/javac/lambda/MethodReferenceParserTest.java	Fri Dec 16 16:41:00 2011 -0800
     2.2 +++ b/test/tools/javac/lambda/MethodReferenceParserTest.java	Mon Dec 19 12:07:07 2011 +0000
     2.3 @@ -24,7 +24,6 @@
     2.4  /*
     2.5   * @test
     2.6   * @bug 7115052
     2.7 - * @ignore 7120266
     2.8   * @summary Add parser support for method references
     2.9   */
    2.10  
    2.11 @@ -45,6 +44,7 @@
    2.12      enum ReferenceKind {
    2.13          METHOD_REF("#Q##Gm"),
    2.14          CONSTRUCTOR_REF("#Q##Gnew"),
    2.15 +        FALSE_REF("min < max"),
    2.16          ERR_SUPER("#Q##Gsuper"),
    2.17          ERR_METH0("#Q##Gm()"),
    2.18          ERR_METH1("#Q##Gm(X)"),
    2.19 @@ -76,6 +76,21 @@
    2.20          }
    2.21      }
    2.22  
    2.23 +    enum ContextKind {
    2.24 +        ASSIGN("SAM s = #E;"),
    2.25 +        METHOD("m(#E, i);");
    2.26 +
    2.27 +        String contextTemplate;
    2.28 +
    2.29 +        ContextKind(String contextTemplate) {
    2.30 +            this.contextTemplate = contextTemplate;
    2.31 +        }
    2.32 +
    2.33 +        String contextString(ExprKind ek, ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk) {
    2.34 +            return contextTemplate.replaceAll("#E", ek.expressionString(rk, qk, gk, sk));
    2.35 +        }
    2.36 +    }
    2.37 +
    2.38      enum GenericKind {
    2.39          NONE(""),
    2.40          ONE("<X>"),
    2.41 @@ -97,7 +112,10 @@
    2.42          UBOUND_SIMPLE("A"),
    2.43          UNBOUND_GENERIC1("A<X>"),
    2.44          UNBOUND_GENERIC2("A<X, Y>"),
    2.45 -        UNBOUND_GENERIC3("A<? extends X, ? super Y>");
    2.46 +        UNBOUND_GENERIC3("A<? extends X, ? super Y>"),
    2.47 +        UNBOUND_GENERIC4("A<int[], short[][]>"),
    2.48 +        NESTED_GENERIC1("A<A<X,Y>, A<X,Y>>"),
    2.49 +        NESTED_GENERIC2("A<A<A<X,Y>,A<X,Y>>, A<A<X,Y>,A<X,Y>>>");
    2.50  
    2.51          String qualifier;
    2.52  
    2.53 @@ -153,7 +171,9 @@
    2.54                  for (GenericKind gk : GenericKind.values()) {
    2.55                      for (SubExprKind sk : SubExprKind.values()) {
    2.56                          for (ExprKind ek : ExprKind.values()) {
    2.57 -                            new MethodReferenceParserTest(rk, qk, gk, sk, ek).run(comp, fm);
    2.58 +                            for (ContextKind ck : ContextKind.values()) {
    2.59 +                                new MethodReferenceParserTest(rk, qk, gk, sk, ek, ck).run(comp, fm);
    2.60 +                            }
    2.61                          }
    2.62                      }
    2.63                  }
    2.64 @@ -167,15 +187,17 @@
    2.65      GenericKind gk;
    2.66      SubExprKind sk;
    2.67      ExprKind ek;
    2.68 +    ContextKind ck;
    2.69      JavaSource source;
    2.70      DiagnosticChecker diagChecker;
    2.71  
    2.72 -    MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek) {
    2.73 +    MethodReferenceParserTest(ReferenceKind rk, QualifierKind qk, GenericKind gk, SubExprKind sk, ExprKind ek, ContextKind ck) {
    2.74          this.rk = rk;
    2.75          this.qk = qk;
    2.76          this.gk = gk;
    2.77          this.sk = sk;
    2.78          this.ek = ek;
    2.79 +        this.ck = ck;
    2.80          this.source = new JavaSource();
    2.81          this.diagChecker = new DiagnosticChecker();
    2.82      }
    2.83 @@ -183,14 +205,16 @@
    2.84      class JavaSource extends SimpleJavaFileObject {
    2.85  
    2.86          String template = "class Test {\n" +
    2.87 -                          "   SAM s = #E;\n" +
    2.88 +                          "   void test() {\n" +
    2.89 +                          "      #C\n" +
    2.90 +                          "   }" +
    2.91                            "}";
    2.92  
    2.93          String source;
    2.94  
    2.95          public JavaSource() {
    2.96              super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
    2.97 -            source = template.replaceAll("#E", ek.expressionString(rk, qk, gk, sk));
    2.98 +            source = template.replaceAll("#C", ck.contextString(ek, rk, qk, gk, sk));
    2.99          }
   2.100  
   2.101          @Override

mercurial