1 /*
2  * Copyright (c) 2012, 2015, 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 8003280
27  * @summary Add lambda tests
28  *   Test SAM conversion of method references in combinations of different contexts,
29  *           lambda body types(statement/expression), boxing/unboxing etc, to verify
30  *           SAM conversion being conducted successfully as expected.
31  * @modules jdk.compiler
32  */
33 
34 import com.sun.source.util.JavacTask;
35 import java.net.URI;
36 import java.util.Arrays;
37 import javax.tools.Diagnostic;
38 import javax.tools.JavaCompiler;
39 import javax.tools.JavaFileObject;
40 import javax.tools.SimpleJavaFileObject;
41 import javax.tools.ToolProvider;
42 import javax.tools.StandardJavaFileManager;
43 
44 public class SamConversionComboTest {
45 
46     enum FInterface {
47         A("A", "interface A { Integer m(int i); }"),
48         B("B", "interface B { int m(Integer i); }"),
49         C("C", "interface C { int m(int i) throws Exception; }");
50 
51         String interfaceType;
52         String interfaceDef;
53 
FInterface(String interfaceType, String interfaceDef)54         FInterface(String interfaceType, String interfaceDef) {
55             this.interfaceType = interfaceType;
56             this.interfaceDef = interfaceDef;
57         }
58     }
59 
60     enum Context {
61         ASSIGNMENT("#FType f = #MR;"),
62         METHOD_CALL("void method1(#FType f) { }\n" +
63                     "void method2() {\n" +
64                     "    method1(#MR);\n" +
65                     "}"),
66         CONSTRUCTOR("X x = new X(#MR);"),
67         RETURN_OF_METHOD("#FType method1() {\n" +
68                          "    return #MR;\n" +
69                          "}"),
70         ARRAY_INITIALIZER("#FType[] oarray = {#MR};"),
71         LAMBDA_BODY("#FType f = n -> ((#FType)#MR).m(n);"),
72         CAST("void test() throws Exception { int n = ((#FType)#MR).m(1); }"),
73         CONDITIONAL_EXPRESSION("#FType f = 2 > 1 ? #MR : null;");
74 
75         String context;
76 
Context(String context)77         Context(String context) {
78             this.context = context;
79         }
80 
getContext(FInterface f, MethodReference mr)81         String getContext(FInterface f, MethodReference mr) {
82             return context.replace("#FType", f.interfaceType).replace("#MR", mr.mrValue);
83         }
84     }
85 
86     enum MethodReference {
87         METHOD1("X::method1"),
88         METHOD2("new X()::method2"),
89         METHOD3("X::method3"),
90         METHOD4("new X()::method4"),
91         METHOD5("new X()::method5"),
92         METHOD6("X::method6"),
93         METHOD7("X::method7"),
94         METHOD8("X::method8");
95 
96         String mrValue;
97 
MethodReference(String mr)98         MethodReference(String mr) {
99             mrValue = mr;
100         }
101     }
102 
103     enum MethodDef {
104         METHOD1("    static Integer method1(int n) {\n" +
105                 "        return n + 1;\n" +
106                 "    }\n", 0),
107         METHOD2("    int method2(Integer n) {\n" +
108                 "        return value == 0 ? n + 2 : n + value;\n" +
109                 "    }\n", 1),
110         METHOD3("    static int method3(int n) {\n" +
111                 "        return n + 3;\n" +
112                 "    }\n", 2),
113         METHOD4("    Integer method4(Integer n) {\n" +
114                 "        return value == 0 ? n + 4 : n + value;\n" +
115                 "    }\n", 3),
116         METHOD5("    Integer method5(Integer n) {\n" +
117                 "        return value == 0 ? new Integer(n + 5) : new Integer(n + value);\n" +
118                 "    }\n", 4),
119         METHOD6("    static int method6(Integer n) throws Exception{\n" +
120                 "        throw new Exception();\n" +
121                 "    }\n", 5),
122         METHOD7("    static int method7(String s){\n" +
123                 "        return s.length();\n" +
124                 "    }\n", 6),
125         METHOD8("    static String method8(Integer n){\n" +
126                 "        return n + \"\";\n" +
127                 "    }\n", 7);
128 
129         String methodStr;
130         int index;
131 
MethodDef(String ms, int i)132         MethodDef(String ms, int i) {
133             methodStr = ms;
134             index = i;
135         }
136 
getMethodReference()137         MethodReference getMethodReference() {
138             return MethodReference.values()[index];
139         }
140     }
141 
142     SourceFile samSourceFile = new SourceFile("FInterface.java", "#C") {
143         public String toString() {
144             String interfaces = "";
145             for(FInterface fi : FInterface.values())
146                 interfaces += fi.interfaceDef + "\n";
147             return template.replace("#C", interfaces);
148         }
149     };
150 
151     String clientTemplate = "class Client {\n" +
152                             "    #Context\n" +
153                             "}\n\n" +
154 
155                             "class X {\n" +
156                             "    int value = 0;\n\n" +
157 
158                             "    X() {\n" +
159                             "    }\n\n" +
160 
161                             "    X(A a) {\n" +
162                             "        value = a.m(9);\n" +
163                             "    }\n\n" +
164 
165                             "    X(B b) {\n" +
166                             "        value = b.m(9);\n" +
167                             "    }\n\n" +
168 
169                             "    X(C c) {\n" +
170                             "        try {\n" +
171                             "            value = c.m(9);\n" +
172                             "        } catch (Exception e){}\n" +
173                             "    }\n\n" +
174 
175                             "#MethodDef" +
176                             "}";
177 
178     SourceFile clientSourceFile = new SourceFile("Client.java", clientTemplate) {
179         public String toString() {
180             return template.replace("#Context", context.getContext(fInterface, methodReference)).replace("#MethodDef", methodDef.methodStr);
181         }
182     };
183 
checkSamConversion()184     boolean checkSamConversion() {
185         if(methodDef == MethodDef.METHOD7 || methodDef == MethodDef.METHOD8)//method signature mismatch
186             return false;
187         if(context != Context.CONSTRUCTOR && fInterface != FInterface.C && methodDef == MethodDef.METHOD6)
188         //method that throws exceptions not thrown by the interface method is a mismatch
189             return false;
190         if(context == Context.CONSTRUCTOR)
191                return false;
192         return true;
193     }
194 
test()195     void test() throws Exception {
196         System.out.println("\n====================================");
197         System.out.println(fInterface + ", " +  context + ", " + methodReference);
198         System.out.println(samSourceFile + "\n" + clientSourceFile);
199 
200         DiagnosticChecker dc = new DiagnosticChecker();
201         JavacTask ct = (JavacTask)comp.getTask(null, fm, dc, null, null, Arrays.asList(samSourceFile, clientSourceFile));
202         ct.analyze();
203         if (dc.errorFound == checkSamConversion()) {
204             throw new AssertionError(samSourceFile + "\n\n" + clientSourceFile);
205         }
206         count++;
207     }
208 
209     abstract class SourceFile extends SimpleJavaFileObject {
210 
211         protected String template;
212 
SourceFile(String filename, String template)213         public SourceFile(String filename, String template) {
214             super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
215             this.template = template;
216         }
217 
218         @Override
getCharContent(boolean ignoreEncodingErrors)219         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
220             return toString();
221         }
222 
toString()223         public abstract String toString();
224     }
225 
226     static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
227 
228         boolean errorFound = false;
229 
report(Diagnostic<? extends JavaFileObject> diagnostic)230         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
231             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
232                 errorFound = true;
233             }
234         }
235     }
236 
237     FInterface fInterface;
238     Context context;
239     MethodDef methodDef;
240     MethodReference methodReference;
241     static int count = 0;
242 
243     static JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
244     static StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null);
245 
SamConversionComboTest(FInterface f, Context c, MethodDef md)246     SamConversionComboTest(FInterface f, Context c, MethodDef md) {
247         fInterface = f;
248         context = c;
249         methodDef = md;
250         methodReference = md.getMethodReference();
251     }
252 
main(String[] args)253     public static void main(String[] args) throws Exception {
254         try {
255             for(Context ct : Context.values()) {
256                 for (FInterface fi : FInterface.values()) {
257                     for (MethodDef md: MethodDef.values()) {
258                         new SamConversionComboTest(fi, ct, md).test();
259                     }
260                 }
261             }
262             System.out.println("total tests: " + count);
263         } finally {
264             fm.close();
265         }
266     }
267 }
268