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