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  *  Check that self/forward references from lambda expressions behave
29  *          consistently w.r.t. local inner classes
30  * @modules jdk.compiler
31  */
32 
33 import java.net.URI;
34 import java.util.Arrays;
35 import javax.tools.Diagnostic;
36 import javax.tools.JavaCompiler;
37 import javax.tools.JavaFileObject;
38 import javax.tools.SimpleJavaFileObject;
39 import javax.tools.StandardJavaFileManager;
40 import javax.tools.ToolProvider;
41 import com.sun.source.util.JavacTask;
42 
43 public class TestSelfRef {
44 
45     static int checkCount = 0;
46 
47     enum RefKind {
48         SELF_LAMBDA("SAM s = x->{ System.out.println(s); };", true, false),
49         FORWARD_LAMBDA("SAM s = x->{ System.out.println(f); };\nObject f = null;", false, true),
50         SELF_ANON("Object s = new Object() { void test() { System.out.println(s); } };", true, false),
51         FORWARD_ANON("Object s = new Object() { void test() { System.out.println(f); } }; Object f = null;", false, true);
52 
53         String refStr;
54         boolean selfRef;
55         boolean forwardRef;
56 
RefKind(String refStr, boolean selfRef, boolean forwardRef)57         private RefKind(String refStr, boolean selfRef, boolean forwardRef) {
58             this.refStr = refStr;
59             this.selfRef = selfRef;
60             this.forwardRef = forwardRef;
61         }
62     }
63 
64     enum EnclosingKind {
65         TOPLEVEL("class C { #S }"),
66         MEMBER_INNER("class Outer { class C { #S } }"),
67         NESTED_INNER("class Outer { static class C { #S } }");
68 
69         String enclStr;
70 
EnclosingKind(String enclStr)71         private EnclosingKind(String enclStr) {
72             this.enclStr = enclStr;
73         }
74     }
75 
76     enum InnerKind {
77         NONE("#R"),
78         LOCAL_NONE("class Local { #R }"),
79         LOCAL_MTH("class Local { void test() { #R } }"),
80         ANON_NONE("new Object() { #R };"),
81         ANON_MTH("new Object() { void test() { #R } };");
82 
83         String innerStr;
84 
InnerKind(String innerStr)85         private InnerKind(String innerStr) {
86             this.innerStr = innerStr;
87         }
88 
inMethodContext(SiteKind sk)89         boolean inMethodContext(SiteKind sk) {
90             switch (this) {
91                 case LOCAL_MTH:
92                 case ANON_MTH: return true;
93                 case NONE: return sk != SiteKind.NONE;
94                 default:
95                     return false;
96             }
97         }
98     }
99 
100     enum SiteKind {
101         NONE("#I"),
102         STATIC_INIT("static { #I }"),
103         INSTANCE_INIT("{ #I }"),
104         CONSTRUCTOR("C() { #I }"),
105         METHOD("void test() { #I }");
106 
107         String siteStr;
108 
SiteKind(String siteStr)109         private SiteKind(String siteStr) {
110             this.siteStr = siteStr;
111         }
112     }
113 
main(String... args)114     public static void main(String... args) throws Exception {
115 
116         //create default shared JavaCompiler - reused across multiple compilations
117         JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
118         try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
119 
120             for (EnclosingKind ek : EnclosingKind.values()) {
121                 for (SiteKind sk : SiteKind.values()) {
122                     if (sk == SiteKind.STATIC_INIT && ek == EnclosingKind.MEMBER_INNER)
123                         continue;
124                     for (InnerKind ik : InnerKind.values()) {
125                         if (ik != InnerKind.NONE && sk == SiteKind.NONE)
126                             break;
127                         for (RefKind rk : RefKind.values()) {
128                             new TestSelfRef(ek, sk, ik, rk).run(comp, fm);
129                         }
130                     }
131                 }
132             }
133             System.out.println("Total check executed: " + checkCount);
134         }
135     }
136 
137     EnclosingKind ek;
138     SiteKind sk;
139     InnerKind ik;
140     RefKind rk;
141     JavaSource source;
142     DiagnosticChecker diagChecker;
143 
TestSelfRef(EnclosingKind ek, SiteKind sk, InnerKind ik, RefKind rk)144     TestSelfRef(EnclosingKind ek, SiteKind sk, InnerKind ik, RefKind rk) {
145         this.ek = ek;
146         this.sk = sk;
147         this.ik = ik;
148         this.rk = rk;
149         this.source = new JavaSource();
150         this.diagChecker = new DiagnosticChecker();
151     }
152 
153     class JavaSource extends SimpleJavaFileObject {
154 
155         String bodyTemplate = "interface SAM { void test(Object o); }\n#B";
156         String source;
157 
JavaSource()158         public JavaSource() {
159             super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
160             source = bodyTemplate.replace("#B",
161                     ek.enclStr.replace("#S", sk.siteStr.replace("#I", ik.innerStr.replace("#R", rk.refStr))));
162         }
163 
164         @Override
getCharContent(boolean ignoreEncodingErrors)165         public CharSequence getCharContent(boolean ignoreEncodingErrors) {
166             return source;
167         }
168     }
169 
run(JavaCompiler tool, StandardJavaFileManager fm)170     void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception {
171         JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker,
172                 null, null, Arrays.asList(source));
173         try {
174             ct.analyze();
175         } catch (Throwable ex) {
176             throw new AssertionError("Error thron when compiling the following code:\n" + source.getCharContent(true));
177         }
178         check();
179     }
180 
isErrorExpected()181     boolean isErrorExpected() {
182         //illegal forward ref
183         boolean result = ik.inMethodContext(sk) && (rk.selfRef || rk.forwardRef);
184         result |= (rk == RefKind.SELF_LAMBDA || rk == RefKind.FORWARD_LAMBDA);
185         return result;
186     }
187 
check()188     void check() {
189         checkCount++;
190         boolean errorExpected = isErrorExpected();
191         if (diagChecker.errorFound != errorExpected) {
192             throw new Error("invalid diagnostics for source:\n" +
193                 source.getCharContent(true) +
194                 "\nFound error: " + diagChecker.errorFound +
195                 "\nExpected error: " + errorExpected);
196         }
197     }
198 
199     static class DiagnosticChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
200 
201         boolean errorFound;
202 
report(Diagnostic<? extends JavaFileObject> diagnostic)203         public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
204             if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
205                 errorFound = true;
206             }
207         }
208     }
209 }
210