| /* |
| * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /** |
| * @test |
| * @bug 8003280 8004102 8006694 |
| * @summary Add lambda tests |
| * perform several automated checks in lambda conversion, esp. around accessibility |
| * temporarily workaround combo tests are causing time out in several platforms |
| * @author Maurizio Cimadamore |
| * @library ../lib |
| * @build JavacTestingAbstractThreadedTest |
| * @run main/timeout=600/othervm FunctionalInterfaceConversionTest |
| */ |
| |
| // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) |
| // see JDK-8006746 |
| |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.Arrays; |
| import javax.tools.Diagnostic; |
| import javax.tools.JavaCompiler; |
| import javax.tools.JavaFileObject; |
| import javax.tools.SimpleJavaFileObject; |
| import javax.tools.ToolProvider; |
| import com.sun.source.util.JavacTask; |
| |
| public class FunctionalInterfaceConversionTest |
| extends JavacTestingAbstractThreadedTest |
| implements Runnable { |
| |
| enum PackageKind { |
| NO_PKG(""), |
| PKG_A("a"); |
| |
| String pkg; |
| |
| PackageKind(String pkg) { |
| this.pkg = pkg; |
| } |
| |
| String getPkgDecl() { |
| return this == NO_PKG ? |
| "" : |
| "package " + pkg + ";"; |
| } |
| |
| String getImportStat() { |
| return this == NO_PKG ? |
| "" : |
| "import " + pkg + ".*;"; |
| } |
| } |
| |
| enum SamKind { |
| CLASS("public class Sam { }"), |
| ABSTACT_CLASS("public abstract class Sam { }"), |
| ANNOTATION("public @interface Sam { }"), |
| ENUM("public enum Sam { }"), |
| INTERFACE("public interface Sam { \n #METH; \n }"); |
| |
| String sam_str; |
| |
| SamKind(String sam_str) { |
| this.sam_str = sam_str; |
| } |
| |
| String getSam(String methStr) { |
| return sam_str.replaceAll("#METH", methStr); |
| } |
| } |
| |
| enum ModifierKind { |
| PUBLIC("public"), |
| PACKAGE(""); |
| |
| String modifier_str; |
| |
| ModifierKind(String modifier_str) { |
| this.modifier_str = modifier_str; |
| } |
| |
| boolean stricterThan(ModifierKind that) { |
| return this.ordinal() > that.ordinal(); |
| } |
| } |
| |
| enum TypeKind { |
| EXCEPTION("Exception"), |
| PKG_CLASS("PackageClass"); |
| |
| String typeStr; |
| |
| private TypeKind(String typeStr) { |
| this.typeStr = typeStr; |
| } |
| } |
| |
| enum ExprKind { |
| LAMBDA("x -> null"), |
| MREF("this::m"); |
| |
| String exprStr; |
| |
| private ExprKind(String exprStr) { |
| this.exprStr = exprStr; |
| } |
| } |
| |
| enum MethodKind { |
| NONE(""), |
| NON_GENERIC("public abstract #R m(#ARG s) throws #T;"), |
| GENERIC("public abstract <X> #R m(#ARG s) throws #T;"); |
| |
| String methodTemplate; |
| |
| private MethodKind(String methodTemplate) { |
| this.methodTemplate = methodTemplate; |
| } |
| |
| String getMethod(TypeKind retType, TypeKind argType, TypeKind thrownType) { |
| return methodTemplate.replaceAll("#R", retType.typeStr). |
| replaceAll("#ARG", argType.typeStr). |
| replaceAll("#T", thrownType.typeStr); |
| } |
| } |
| |
| public static void main(String[] args) throws Exception { |
| for (PackageKind samPkg : PackageKind.values()) { |
| for (ModifierKind modKind : ModifierKind.values()) { |
| for (SamKind samKind : SamKind.values()) { |
| for (MethodKind samMeth : MethodKind.values()) { |
| for (MethodKind clientMeth : MethodKind.values()) { |
| for (TypeKind retType : TypeKind.values()) { |
| for (TypeKind argType : TypeKind.values()) { |
| for (TypeKind thrownType : TypeKind.values()) { |
| for (ExprKind exprKind : ExprKind.values()) { |
| pool.execute( |
| new FunctionalInterfaceConversionTest( |
| samPkg, modKind, samKind, |
| samMeth, clientMeth, retType, |
| argType, thrownType, exprKind)); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| checkAfterExec(false); |
| } |
| |
| PackageKind samPkg; |
| ModifierKind modKind; |
| SamKind samKind; |
| MethodKind samMeth; |
| MethodKind clientMeth; |
| TypeKind retType; |
| TypeKind argType; |
| TypeKind thrownType; |
| ExprKind exprKind; |
| DiagnosticChecker dc; |
| |
| SourceFile samSourceFile = new SourceFile("Sam.java", "#P \n #C") { |
| @Override |
| public String toString() { |
| return template.replaceAll("#P", samPkg.getPkgDecl()). |
| replaceAll("#C", samKind.getSam( |
| samMeth.getMethod(retType, argType, thrownType))); |
| } |
| }; |
| |
| SourceFile pkgClassSourceFile = |
| new SourceFile("PackageClass.java", |
| "#P\n #M class PackageClass extends Exception { }") { |
| @Override |
| public String toString() { |
| return template.replaceAll("#P", samPkg.getPkgDecl()). |
| replaceAll("#M", modKind.modifier_str); |
| } |
| }; |
| |
| SourceFile clientSourceFile = |
| new SourceFile("Client.java", |
| "#I\n abstract class Client { \n" + |
| " Sam s = #E;\n" + |
| " #M \n }") { |
| @Override |
| public String toString() { |
| return template.replaceAll("#I", samPkg.getImportStat()) |
| .replaceAll("#E", exprKind.exprStr) |
| .replaceAll("#M", clientMeth.getMethod(retType, argType, thrownType)); |
| } |
| }; |
| |
| FunctionalInterfaceConversionTest(PackageKind samPkg, ModifierKind modKind, |
| SamKind samKind, MethodKind samMeth, MethodKind clientMeth, |
| TypeKind retType, TypeKind argType, TypeKind thrownType, |
| ExprKind exprKind) { |
| this.samPkg = samPkg; |
| this.modKind = modKind; |
| this.samKind = samKind; |
| this.samMeth = samMeth; |
| this.clientMeth = clientMeth; |
| this.retType = retType; |
| this.argType = argType; |
| this.thrownType = thrownType; |
| this.exprKind = exprKind; |
| this.dc = new DiagnosticChecker(); |
| } |
| |
| @Override |
| public void run() { |
| final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); |
| |
| JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), dc, null, null, |
| Arrays.asList(samSourceFile, pkgClassSourceFile, clientSourceFile)); |
| try { |
| ct.analyze(); |
| } catch (IOException ex) { |
| throw new AssertionError("Test failing with cause", ex.getCause()); |
| } |
| if (dc.errorFound == checkSamConversion()) { |
| throw new AssertionError(samSourceFile + "\n\n" + |
| pkgClassSourceFile + "\n\n" + clientSourceFile); |
| } |
| } |
| |
| boolean checkSamConversion() { |
| if (samKind != SamKind.INTERFACE) { |
| //sam type must be an interface |
| return false; |
| } else if (samMeth == MethodKind.NONE) { |
| //interface must have at least a method |
| return false; |
| } else if (exprKind == ExprKind.LAMBDA && |
| samMeth != MethodKind.NON_GENERIC) { |
| //target method for lambda must be non-generic |
| return false; |
| } else if (exprKind == ExprKind.MREF && |
| clientMeth == MethodKind.NONE) { |
| return false; |
| } else if (samPkg != PackageKind.NO_PKG && |
| modKind != ModifierKind.PUBLIC && |
| (retType == TypeKind.PKG_CLASS || |
| argType == TypeKind.PKG_CLASS || |
| thrownType == TypeKind.PKG_CLASS)) { |
| //target must not contain inaccessible types |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| abstract class SourceFile extends SimpleJavaFileObject { |
| |
| protected String template; |
| |
| public SourceFile(String filename, String template) { |
| super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE); |
| this.template = template; |
| } |
| |
| @Override |
| public CharSequence getCharContent(boolean ignoreEncodingErrors) { |
| return toString(); |
| } |
| |
| @Override |
| public abstract String toString(); |
| } |
| |
| static class DiagnosticChecker |
| implements javax.tools.DiagnosticListener<JavaFileObject> { |
| |
| boolean errorFound = false; |
| |
| @Override |
| public void report(Diagnostic<? extends JavaFileObject> diagnostic) { |
| if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { |
| errorFound = true; |
| } |
| } |
| } |
| } |