1/* 2 * Copyright (c) 2016, 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 8166628 27 * @summary Verify that loading a classfile for a local class that is a member of an anonymous class 28 * won't break compilation. 29 * @modules jdk.compiler 30 */ 31 32import java.io.IOException; 33import java.io.StringWriter; 34import java.net.URI; 35import java.net.URISyntaxException; 36import java.nio.file.Path; 37import java.nio.file.Paths; 38import java.util.Arrays; 39import java.util.List; 40import java.util.Set; 41 42import javax.annotation.processing.AbstractProcessor; 43import javax.annotation.processing.RoundEnvironment; 44import javax.annotation.processing.SupportedAnnotationTypes; 45import javax.lang.model.element.Element; 46import javax.lang.model.element.PackageElement; 47import javax.lang.model.element.TypeElement; 48import javax.lang.model.util.Elements; 49import javax.tools.Diagnostic; 50import javax.tools.DiagnosticListener; 51import javax.tools.JavaCompiler; 52import javax.tools.JavaFileObject; 53import javax.tools.SimpleJavaFileObject; 54import javax.tools.ToolProvider; 55 56import com.sun.source.tree.BlockTree; 57import com.sun.source.tree.ClassTree; 58import com.sun.source.tree.MethodTree; 59import com.sun.source.tree.Tree; 60import com.sun.source.tree.VariableTree; 61import com.sun.source.util.JavacTask; 62import com.sun.source.util.TaskEvent; 63import com.sun.source.util.TaskEvent.Kind; 64import com.sun.source.util.TaskListener; 65import com.sun.source.util.TreePath; 66import com.sun.source.util.TreePathScanner; 67import com.sun.source.util.Trees; 68 69public class LocalInAnonymous { 70 71 public static void main(String[] args) throws Exception { 72 Path base = Paths.get(".").toAbsolutePath(); 73 Path classes = base.resolve("classes"); 74 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 75 DiagnosticListener<JavaFileObject> noErrors = d -> { 76 if (d.getKind() == Diagnostic.Kind.ERROR) { 77 throw new AssertionError(d.getMessage(null)); 78 } 79 }; 80 List<TJFO> files = Arrays.asList(new TJFO("Test", CODE)); 81 List<String> options = Arrays.asList("-d", classes.toString()); 82 StringWriter out = new StringWriter(); 83 JavacTask task = (JavacTask) compiler.getTask(out, null, noErrors, options, null, files); 84 task.call(); 85 if (!out.toString().isEmpty()) { 86 throw new AssertionError("Unexpected output: " + out); 87 } 88 options = Arrays.asList("-classpath", classes.toString(), "-d", classes.toString()); 89 JavacTask task2 = (JavacTask) compiler.getTask(out, null, noErrors, options, null, files); 90 task2.addTaskListener(new TaskListener() { 91 @Override 92 public void started(TaskEvent te) { 93 } 94 @Override 95 public void finished(TaskEvent te) { 96 if (te.getKind() == Kind.ENTER) { 97 Element pack = task2.getElements().getTypeElement("Test").getEnclosingElement(); 98 System.err.println(pack.getEnclosedElements()); 99 } 100 if (te.getKind() == Kind.ANALYZE) { 101 PackageElement pack = task2.getElements().getPackageOf(te.getTypeElement()); 102 new OwnerCheck(Trees.instance(task2), pack).scan(te.getCompilationUnit(), null); 103 } 104 } 105 }); 106 task2.call(); 107 if (!out.toString().isEmpty()) { 108 throw new AssertionError("Unexpected output: " + out); 109 } 110 options = Arrays.asList("-classpath", classes.toString(), 111 "-d", classes.toString(), 112 "-processorpath", System.getProperty("test.classes"), 113 "-processor", Processor.class.getName()); 114 JavacTask task3 = (JavacTask) compiler.getTask(out, null, noErrors, options, null, files); 115 task3.call(); 116 if (!out.toString().isEmpty()) { 117 throw new AssertionError("Unexpected output: " + out); 118 } 119 } 120 121 private static final class TJFO extends SimpleJavaFileObject { 122 123 private final String code; 124 125 public TJFO(String name, String code) throws URISyntaxException { 126 super(new URI("mem:///" + name + ".java"), Kind.SOURCE); 127 this.code = code; 128 } 129 130 @Override 131 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 132 return code; 133 } 134 135 } 136 137 @SupportedAnnotationTypes("*") 138 public static final class Processor extends AbstractProcessor { 139 140 @Override 141 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 142 Trees trees = Trees.instance(processingEnv); 143 Elements elements = processingEnv.getElementUtils(); 144 Element pack = elements.getTypeElement("Test").getEnclosingElement(); 145 for (Element root : pack.getEnclosedElements()) { 146 TreePath tp = trees.getPath(root); 147 new OwnerCheck(trees, pack).scan(tp.getCompilationUnit(), null); 148 149 } 150 return false; 151 } 152 153 } 154 155 private static final class OwnerCheck extends TreePathScanner<Void, Void> { 156 private final Trees trees; 157 private Element currentOwner; 158 159 public OwnerCheck(Trees trees, Element pack) { 160 this.trees = trees; 161 this.currentOwner = pack; 162 } 163 164 @Override 165 public Void visitClass(ClassTree node, Void p) { 166 Element prevOwner = currentOwner; 167 try { 168 Element currentElement = trees.getElement(getCurrentPath()); 169 if (currentOwner != null && currentElement.getEnclosingElement() != currentOwner) { 170 throw new AssertionError("Unexpected owner!"); 171 } 172 currentOwner = currentElement; 173 return super.visitClass(node, p); 174 } finally { 175 currentOwner = prevOwner; 176 } 177 } 178 179 @Override 180 public Void visitMethod(MethodTree node, Void p) { 181 Element prevOwner = currentOwner; 182 try { 183 Element currentElement = trees.getElement(getCurrentPath()); 184 if (currentElement.getEnclosingElement() != currentOwner) { 185 throw new AssertionError("Unexpected owner!"); 186 } 187 currentOwner = currentElement; 188 return super.visitMethod(node, p); 189 } finally { 190 currentOwner = prevOwner; 191 } 192 } 193 194 @Override 195 public Void visitVariable(VariableTree node, Void p) { 196 Element currentElement = trees.getElement(getCurrentPath()); 197 if (!currentElement.getKind().isField()) { 198 return super.visitVariable(node, p); 199 } 200 Element prevOwner = currentOwner; 201 try { 202 if (currentElement.getEnclosingElement() != currentOwner) { 203 throw new AssertionError("Unexpected owner!"); 204 } 205 currentOwner = currentElement; 206 return super.visitVariable(node, p); 207 } finally { 208 currentOwner = prevOwner; 209 } 210 } 211 212 @Override 213 public Void visitBlock(BlockTree node, Void p) { 214 if (getCurrentPath().getParentPath().getLeaf().getKind() != Tree.Kind.CLASS) { 215 return super.visitBlock(node, p); 216 } 217 Element prevOwner = currentOwner; 218 try { 219 currentOwner = null; 220 return super.visitBlock(node, p); 221 } finally { 222 currentOwner = prevOwner; 223 } 224 } 225 226 } 227 228 private static final String CODE = 229 "public class Test {\n" + 230 " void test() {\n" + 231 " Object o = new Object() {\n" + 232 " class IC {}\n" + 233 " public Object get() {\n" + 234 " return new IC();\n" + 235 " }\n" + 236 " };\n" + 237 " }\n" + 238 " {\n" + 239 " Object o = new Object() {\n" + 240 " class IC {}\n" + 241 " public Object get() {\n" + 242 " return new IC();\n" + 243 " }\n" + 244 " };\n" + 245 " }\n" + 246 " static {\n" + 247 " Object o = new Object() {\n" + 248 " class IC {}\n" + 249 " public Object get() {\n" + 250 " return new IC();\n" + 251 " }\n" + 252 " };\n" + 253 " }\n" + 254 " Object o1 = new Object() {\n" + 255 " class IC {}\n" + 256 " public Object get() {\n" + 257 " return new IC();\n" + 258 " }\n" + 259 " };\n" + 260 " static Object o2 = new Object() {\n" + 261 " class IC {}\n" + 262 " public Object get() {\n" + 263 " return new IC();\n" + 264 " }\n" + 265 " };\n" + 266 "}"; 267} 268