langtools/test/tools/javac/lambda/LambdaParserTest.java
changeset 11143 9dbe313bfb74
child 11381 890ea587d133
equal deleted inserted replaced
11142:45d0ec1e7463 11143:9dbe313bfb74
       
     1 /*
       
     2  * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /*
       
    25  * @test
       
    26  * @bug 7115050
       
    27  * @summary Add parser support for lambda expressions
       
    28  */
       
    29 
       
    30 import com.sun.source.util.JavacTask;
       
    31 import java.net.URI;
       
    32 import java.util.Arrays;
       
    33 import javax.tools.Diagnostic;
       
    34 import javax.tools.JavaCompiler;
       
    35 import javax.tools.JavaFileObject;
       
    36 import javax.tools.SimpleJavaFileObject;
       
    37 import javax.tools.StandardJavaFileManager;
       
    38 import javax.tools.ToolProvider;
       
    39 
       
    40 public class LambdaParserTest {
       
    41 
       
    42     static int checkCount = 0;
       
    43 
       
    44     enum LambdaKind {
       
    45         NILARY_EXPR("()->x"),
       
    46         NILARY_STMT("()->{ return x; }"),
       
    47         ONEARY_SHORT_EXPR("x->x"),
       
    48         ONEARY_SHORT_STMT("x->{ return x; }"),
       
    49         ONEARY_EXPR("(#M1 #T1 x)->x"),
       
    50         ONEARY_STMT("(#M1 #T1 x)->{ return x; }"),
       
    51         TWOARY_EXPR("(#M1 #T1 x, #M2 #T2 y)->x"),
       
    52         TWOARY_STMT("(#M1 #T1 x, #M2 #T2 y)->{ return x; }");
       
    53 
       
    54         String lambdaTemplate;
       
    55 
       
    56         LambdaKind(String lambdaTemplate) {
       
    57             this.lambdaTemplate = lambdaTemplate;
       
    58         }
       
    59 
       
    60         String getLambdaString(LambdaParameterKind pk1, LambdaParameterKind pk2,
       
    61                 ModifierKind mk1, ModifierKind mk2) {
       
    62             return lambdaTemplate.replaceAll("#M1", mk1.modifier)
       
    63                     .replaceAll("#M2", mk2.modifier)
       
    64                     .replaceAll("#T1", pk1.parameterType)
       
    65                     .replaceAll("#T2", pk2.parameterType);
       
    66         }
       
    67 
       
    68         int arity() {
       
    69             switch (this) {
       
    70                 case NILARY_EXPR:
       
    71                 case NILARY_STMT: return 0;
       
    72                 case ONEARY_SHORT_EXPR:
       
    73                 case ONEARY_SHORT_STMT:
       
    74                 case ONEARY_EXPR:
       
    75                 case ONEARY_STMT: return 1;
       
    76                 case TWOARY_EXPR:
       
    77                 case TWOARY_STMT: return 2;
       
    78                 default: throw new AssertionError("Invalid lambda kind " + this);
       
    79             }
       
    80         }
       
    81 
       
    82         boolean isShort() {
       
    83             return this == ONEARY_SHORT_EXPR ||
       
    84                     this == ONEARY_SHORT_STMT;
       
    85         }
       
    86     }
       
    87 
       
    88     enum LambdaParameterKind {
       
    89         IMPLICIT(""),
       
    90         EXPLIICT_SIMPLE("A"),
       
    91         EXPLICIT_VARARGS("A..."),
       
    92         EXPLICIT_GENERIC1("A<X>"),
       
    93         EXPLICIT_GENERIC3("A<? extends X, ? super Y>");
       
    94 
       
    95         String parameterType;
       
    96 
       
    97         LambdaParameterKind(String parameterType) {
       
    98             this.parameterType = parameterType;
       
    99         }
       
   100 
       
   101         boolean explicit() {
       
   102             return this != IMPLICIT;
       
   103         }
       
   104     }
       
   105 
       
   106     enum ModifierKind {
       
   107         NONE(""),
       
   108         FINAL("final"),
       
   109         PUBLIC("public");
       
   110 
       
   111         String modifier;
       
   112 
       
   113         ModifierKind(String modifier) {
       
   114             this.modifier = modifier;
       
   115         }
       
   116 
       
   117         boolean compatibleWith(LambdaParameterKind pk) {
       
   118             switch (this) {
       
   119                 case PUBLIC: return false;
       
   120                 case FINAL: return pk != LambdaParameterKind.IMPLICIT;
       
   121                 case NONE: return true;
       
   122                 default: throw new AssertionError("Invalid modifier kind " + this);
       
   123             }
       
   124         }
       
   125     }
       
   126 
       
   127     enum ExprKind {
       
   128         NONE("#L#S"),
       
   129         SINGLE_PAREN1("(#L#S)"),
       
   130         SINGLE_PAREN2("(#L)#S"),
       
   131         DOUBLE_PAREN1("((#L#S))"),
       
   132         DOUBLE_PAREN2("((#L)#S)"),
       
   133         DOUBLE_PAREN3("((#L))#S");
       
   134 
       
   135         String expressionTemplate;
       
   136 
       
   137         ExprKind(String expressionTemplate) {
       
   138             this.expressionTemplate = expressionTemplate;
       
   139         }
       
   140 
       
   141         String expressionString(LambdaParameterKind pk1, LambdaParameterKind pk2,
       
   142                 ModifierKind mk1, ModifierKind mk2, LambdaKind lk, SubExprKind sk) {
       
   143             return expressionTemplate.replaceAll("#L", lk.getLambdaString(pk1, pk2, mk1, mk2))
       
   144                     .replaceAll("#S", sk.subExpression);
       
   145         }
       
   146     }
       
   147 
       
   148     enum SubExprKind {
       
   149         NONE(""),
       
   150         SELECT_FIELD(".f"),
       
   151         SELECT_METHOD(".f()"),
       
   152         SELECT_NEW(".new Foo()"),
       
   153         POSTINC("++"),
       
   154         POSTDEC("--");
       
   155 
       
   156         String subExpression;
       
   157 
       
   158         SubExprKind(String subExpression) {
       
   159             this.subExpression = subExpression;
       
   160         }
       
   161     }
       
   162 
       
   163     public static void main(String... args) throws Exception {
       
   164 
       
   165         //create default shared JavaCompiler - reused across multiple compilations
       
   166         JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
       
   167         StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
       
   168 
       
   169         for (LambdaKind lk : LambdaKind.values()) {
       
   170             for (LambdaParameterKind pk1 : LambdaParameterKind.values()) {
       
   171                 if (lk.arity() < 1 && pk1 != LambdaParameterKind.IMPLICIT) continue;
       
   172                 for (LambdaParameterKind pk2 : LambdaParameterKind.values()) {
       
   173                     if (lk.arity() < 2 && pk2 != LambdaParameterKind.IMPLICIT) continue;
       
   174                     for (ModifierKind mk1 : ModifierKind.values()) {
       
   175                         if (mk1 != ModifierKind.NONE && lk.isShort()) continue;
       
   176                         if (lk.arity() < 1 && mk1 != ModifierKind.NONE) continue;
       
   177                         for (ModifierKind mk2 : ModifierKind.values()) {
       
   178                             if (lk.arity() < 2 && mk2 != ModifierKind.NONE) continue;
       
   179                             for (SubExprKind sk : SubExprKind.values()) {
       
   180                                 for (ExprKind ek : ExprKind.values()) {
       
   181                                     new LambdaParserTest(pk1, pk2, mk1, mk2, lk, sk, ek)
       
   182                                             .run(comp, fm);
       
   183                                 }
       
   184                             }
       
   185                         }
       
   186                     }
       
   187                 }
       
   188             }
       
   189         }
       
   190         System.out.println("Total check executed: " + checkCount);
       
   191     }
       
   192 
       
   193     LambdaParameterKind pk1;
       
   194     LambdaParameterKind pk2;
       
   195     ModifierKind mk1;
       
   196     ModifierKind mk2;
       
   197     LambdaKind lk;
       
   198     SubExprKind sk;
       
   199     ExprKind ek;
       
   200     JavaSource source;
       
   201     DiagnosticChecker diagChecker;
       
   202 
       
   203     LambdaParserTest(LambdaParameterKind pk1, LambdaParameterKind pk2, ModifierKind mk1,
       
   204             ModifierKind mk2, LambdaKind lk, SubExprKind sk, ExprKind ek) {
       
   205         this.pk1 = pk1;
       
   206         this.pk2 = pk2;
       
   207         this.mk1 = mk1;
       
   208         this.mk2 = mk2;
       
   209         this.lk = lk;
       
   210         this.sk = sk;
       
   211         this.ek = ek;
       
   212         this.source = new JavaSource();
       
   213         this.diagChecker = new DiagnosticChecker();
       
   214     }
       
   215 
       
   216     class JavaSource extends SimpleJavaFileObject {
       
   217 
       
   218         String template = "class Test {\n" +
       
   219                           "   SAM s = #E;\n" +
       
   220                           "}";
       
   221 
       
   222         String source;
       
   223 
       
   224         public JavaSource() {
       
   225             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
       
   226             source = template.replaceAll("#E", ek.expressionString(pk1, pk2, mk1, mk2, lk, sk));
       
   227         }
       
   228 
       
   229         @Override
       
   230         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
       
   231             return source;
       
   232         }
       
   233     }
       
   234 
       
   235     void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
       
   236         JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
       
   237                 Arrays.asList("-XDallowLambda"), null, Arrays.asList(source));
       
   238         try {
       
   239             ct.parse();
       
   240         } catch (Throwable ex) {
       
   241             throw new AssertionError("Error thron when parsing the following source:\n" + source.getCharContent(true));
       
   242         }
       
   243         check();
       
   244     }
       
   245 
       
   246     void check() {
       
   247         checkCount++;
       
   248 
       
   249         boolean errorExpected = (lk.arity() > 0 && !mk1.compatibleWith(pk1)) ||
       
   250                 (lk.arity() > 1 && !mk2.compatibleWith(pk2));
       
   251 
       
   252         if (lk.arity() == 2 &&
       
   253                 (pk1.explicit() != pk2.explicit() ||
       
   254                 pk1 == LambdaParameterKind.EXPLICIT_VARARGS)) {
       
   255             errorExpected = true;
       
   256         }
       
   257 
       
   258         if (errorExpected != diagChecker.errorFound) {
       
   259             throw new Error("invalid diagnostics for source:\n" +
       
   260                 source.getCharContent(true) +
       
   261                 "\nFound error: " + diagChecker.errorFound +
       
   262                 "\nExpected error: " + errorExpected);
       
   263         }
       
   264     }
       
   265 
       
   266     static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
       
   267 
       
   268         boolean errorFound;
       
   269 
       
   270         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
       
   271             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
       
   272                 errorFound = true;
       
   273             }
       
   274         }
       
   275     }
       
   276 }