/*
* Copyright (c) 2011, 2013, 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 8006694
* @summary Add lambda tests
* perform automated checks in type inference in lambda expressions
* in different contexts
* temporarily workaround combo tests are causing time out in several platforms
* @library ../../../lib
* @build JavacTestingAbstractThreadedTest
* @compile TypeInferenceComboTest.java
* @run main/othervm/timeout=360 TypeInferenceComboTest
*/
// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047)
// see JDK-8006746
import java.net.URI;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import com.sun.source.util.JavacTask;
public class TypeInferenceComboTest
extends JavacTestingAbstractThreadedTest
implements Runnable {
enum Context {
ASSIGNMENT("SAM#Type s = #LBody;"),
METHOD_CALL("#GenericDeclKind void method1(SAM#Type s) { }\n" +
"void method2() {\n" +
" method1(#LBody);\n" +
"}"),
RETURN_OF_METHOD("SAM#Type method1() {\n" +
" return #LBody;\n" +
"}"),
LAMBDA_RETURN_EXPRESSION("SAM2 s2 = () -> {return (SAM#Type)#LBody;};\n"),
ARRAY_INITIALIZER("Object[] oarray = {\"a\", 1, (SAM#Type)#LBody};");
String context;
Context(String context) {
this.context = context;
}
String getContext(SamKind sk, TypeKind samTargetT, Keyword kw,
TypeKind parameterT, TypeKind returnT, LambdaKind lk,
ParameterKind pk, GenericDeclKind gdk, LambdaBody lb) {
String result = context;
if (sk == SamKind.GENERIC) {
if(this == Context.METHOD_CALL) {
result = result.replaceAll("#GenericDeclKind",
gdk.getGenericDeclKind(samTargetT));
if(gdk == GenericDeclKind.NON_GENERIC)
result = result.replaceAll("#Type", "<" +
samTargetT.typeStr + ">");
else //#GenericDeclKind is <T> or <T extends xxx>
result = result.replaceAll("#Type", "<T>");
}
else {
if(kw == Keyword.VOID)
result = result.replaceAll("#Type", "<" +
samTargetT.typeStr + ">");
else
result = result.replaceAll("#Type", "<? " + kw.keyStr +
" " + samTargetT.typeStr + ">");
}
}
else
result = result.replaceAll("#Type", "").
replaceAll("#GenericDeclKind", "");
return result.replaceAll("#LBody",
lb.getLambdaBody(samTargetT, parameterT, returnT, lk, pk));
}
}
enum SamKind {
GENERIC("interface SAM<T> { #R m(#ARG); }"),
NON_GENERIC("interface SAM { #R m(#ARG); }");
String sam_str;
SamKind(String sam_str) {
this.sam_str = sam_str;
}
String getSam(TypeKind parameterT, TypeKind returnT) {
return sam_str.replaceAll("#ARG",
parameterT == TypeKind.VOID ?
"" : parameterT.typeStr + " arg")
.replaceAll("#R", returnT.typeStr);
}
}
enum TypeKind {
VOID("void", ""),
STRING("String", "\"hello\""),
INTEGER("Integer", "1"),
INT("int", "0"),
COMPARATOR("java.util.Comparator<String>",
"(java.util.Comparator<String>)(a, b) -> a.length()-b.length()"),
SAM("SAM2", "null"),
GENERIC("T", null);
String typeStr;
String valStr;
TypeKind(String typeStr, String valStr) {
this.typeStr = typeStr;
this.valStr = valStr;
}
}
enum LambdaKind {
EXPRESSION("#VAL"),
STATEMENT("{return #VAL;}");
String stmt;
LambdaKind(String stmt) {
this.stmt = stmt;
}
}
enum ParameterKind {
EXPLICIT("#TYPE"),
IMPLICIT("");
String paramTemplate;
ParameterKind(String paramTemplate) {
this.paramTemplate = paramTemplate;
}
}
enum Keyword {
SUPER("super"),
EXTENDS("extends"),
VOID("");
String keyStr;
Keyword(String keyStr) {
this.keyStr = keyStr;
}
}
enum LambdaBody {
//no parameters, return type is one of the TypeKind
RETURN_VOID("() -> #RET"),
//has parameters, return type is one of the TypeKind
RETURN_ARG("(#PK arg) -> #RET");
String bodyStr;
LambdaBody(String bodyStr) {
this.bodyStr = bodyStr;
}
String getLambdaBody(TypeKind samTargetT, TypeKind parameterT,
TypeKind returnT, LambdaKind lk, ParameterKind pk) {
String result = bodyStr.replaceAll("#PK", pk.paramTemplate);
if(result.contains("#TYPE")) {
if (parameterT == TypeKind.GENERIC && this != RETURN_VOID)
result = result.replaceAll("#TYPE",
samTargetT == null? "": samTargetT.typeStr);
else
result = result.replaceAll("#TYPE", parameterT.typeStr);
}
if (this == RETURN_ARG && parameterT == returnT)
return result.replaceAll("#RET", lk.stmt.replaceAll("#VAL", "arg"));
else {
if(returnT != TypeKind.GENERIC)
return result.replaceAll("#RET", lk.stmt.replaceAll("#VAL",
(returnT==TypeKind.VOID &&
lk==LambdaKind.EXPRESSION) ? "{}" : returnT.valStr));
else
return result.replaceAll("#RET",
lk.stmt.replaceAll("#VAL", samTargetT.valStr));
}
}
}
enum GenericDeclKind {
NON_GENERIC(""),
GENERIC_NOBOUND("<T>"),
GENERIC_BOUND("<T extends #ExtendedType>");
String typeStr;
GenericDeclKind(String typeStr) {
this.typeStr = typeStr;
}
String getGenericDeclKind(TypeKind et) {
return typeStr.replaceAll("#ExtendedType", et==null? "":et.typeStr);
}
}
boolean checkTypeInference() {
if (parameterType == TypeKind.VOID) {
if (lambdaBodyType != LambdaBody.RETURN_VOID)
return false;
}
else if (lambdaBodyType != LambdaBody.RETURN_ARG)
return false;
return true;
}
String templateStr = "#C\n" +
"interface SAM2 {\n" +
" SAM m();\n" +
"}\n";
SourceFile samSourceFile = new SourceFile("Sam.java", templateStr) {
public String toString() {
return template.replaceAll("#C",
samKind.getSam(parameterType, returnType));
}
};
SourceFile clientSourceFile = new SourceFile("Client.java",
"class Client { \n" +
" #Context\n" +
"}") {
public String toString() {
return template.replaceAll("#Context",
context.getContext(samKind, samTargetType, keyword,
parameterType, returnType, lambdaKind, parameterKind,
genericDeclKind, lambdaBodyType));
}
};
public void run() {
DiagnosticChecker dc = new DiagnosticChecker();
JavacTask ct = (JavacTask)comp.getTask(null, fm.get(), dc,
null, null, Arrays.asList(samSourceFile, clientSourceFile));
try {
ct.analyze();
} catch (Throwable t) {
processException(t);
}
if (dc.errorFound == checkTypeInference()) {
throw new AssertionError(samSourceFile + "\n\n" +
clientSourceFile + "\n" + parameterType + " " + returnType);
}
}
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();
}
public abstract String toString();
}
static class DiagnosticChecker
implements javax.tools.DiagnosticListener<JavaFileObject> {
boolean errorFound = false;
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
errorFound = true;
}
}
}
SamKind samKind;
TypeKind samTargetType;
TypeKind parameterType;
TypeKind returnType;
Context context;
LambdaBody lambdaBodyType;
LambdaKind lambdaKind;
ParameterKind parameterKind;
Keyword keyword;
GenericDeclKind genericDeclKind;
TypeInferenceComboTest(SamKind sk, TypeKind samTargetT, TypeKind parameterT,
TypeKind returnT, LambdaBody lb, Context c, LambdaKind lk,
ParameterKind pk, Keyword kw, GenericDeclKind gdk) {
samKind = sk;
samTargetType = samTargetT;
parameterType = parameterT;
returnType = returnT;
context = c;
lambdaKind = lk;
parameterKind = pk;
keyword = kw;
lambdaBodyType = lb;
genericDeclKind = gdk;
}
public static void main(String[] args) throws Exception {
for(Context ct : Context.values()) {
for (TypeKind returnT : TypeKind.values()) {
for (TypeKind parameterT : TypeKind.values()) {
for(LambdaBody lb : LambdaBody.values()) {
for (ParameterKind parameterK : ParameterKind.values()) {
for(LambdaKind lambdaK : LambdaKind.values()) {
for (SamKind sk : SamKind.values()) {
if (sk == SamKind.NON_GENERIC) {
generateNonGenericSAM(ct, returnT,
parameterT, lb, parameterK,
lambdaK, sk);
}
else if (sk == SamKind.GENERIC) {
generateGenericSAM(ct, returnT,
parameterT, lb, parameterK,
lambdaK, sk);
}
}
}
}
}
}
}
}
checkAfterExec(false);
}
static void generateNonGenericSAM(Context ct, TypeKind returnT,
TypeKind parameterT, LambdaBody lb, ParameterKind parameterK,
LambdaKind lambdaK, SamKind sk) {
if(parameterT != TypeKind.GENERIC && returnT != TypeKind.GENERIC ) {
pool.execute(new TypeInferenceComboTest(sk, null, parameterT,
returnT, lb, ct, lambdaK, parameterK, null, null));
}
}
static void generateGenericSAM(Context ct, TypeKind returnT,
TypeKind parameterT, LambdaBody lb, ParameterKind parameterK,
LambdaKind lambdaK, SamKind sk) {
for (Keyword kw : Keyword.values()) {
for (TypeKind samTargetT : TypeKind.values()) {
if(samTargetT != TypeKind.VOID &&
samTargetT != TypeKind.INT &&
samTargetT != TypeKind.GENERIC &&
(parameterT == TypeKind.GENERIC ||
returnT == TypeKind.GENERIC)) {
if(ct != Context.METHOD_CALL) {
pool.execute(
new TypeInferenceComboTest(sk, samTargetT, parameterT,
returnT, lb, ct, lambdaK, parameterK, kw, null));
} else {//Context.METHOD_CALL
for (GenericDeclKind gdk :
GenericDeclKind.values())
pool.execute(
new TypeInferenceComboTest(sk, samTargetT,
parameterT, returnT, lb, ct, lambdaK,
parameterK, kw, gdk));
}
}
}
}
}
}