1 /* 2 * Copyright (c) 2018, 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 8187950 27 * @summary Handing of BadClassFile exceptions and CompletionFailures 28 * @library /tools/javac/lib /tools/lib 29 * @modules jdk.compiler/com.sun.tools.javac.api 30 * jdk.compiler/com.sun.tools.javac.main 31 * @build JavacTestingAbstractProcessor MissingClassFile 32 * @run main MissingClassFile 33 */ 34 35 import java.io.OutputStream; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.nio.file.Paths; 39 import java.util.*; 40 import java.util.function.BiConsumer; 41 import java.util.function.Consumer; 42 43 import javax.annotation.processing.*; 44 import javax.lang.model.SourceVersion; 45 import javax.lang.model.element.*; 46 import javax.lang.model.type.DeclaredType; 47 import javax.lang.model.type.TypeKind; 48 import javax.lang.model.type.TypeMirror; 49 import javax.tools.JavaCompiler; 50 import javax.tools.StandardJavaFileManager; 51 import javax.tools.ToolProvider; 52 53 import toolbox.*; 54 import toolbox.Task.*; 55 56 57 import com.sun.source.util.TaskEvent; 58 import com.sun.source.util.TaskListener; 59 60 @SupportedAnnotationTypes("*") 61 public class MissingClassFile { 62 ToolBox tb = new ToolBox(); 63 64 void testPackageContent() throws Exception { 65 Path base = Paths.get("."); 66 Path libClasses = compileLib(base, 67 "package pkg;" + 68 "public class A {" + 69 "}", 70 "package pkg;" + 71 "public class B {" + 72 "}"); 73 74 Files.delete(libClasses.resolve("pkg/B.class")); 75 try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) { 76 out.write(0); 77 } 78 79 doRunTest(base, 80 t -> { 81 PackageElement pe = t.getElements().getPackageElement("pkg"); 82 for (Element el : pe.getEnclosedElements()) { 83 verifyElement(t, el); 84 } 85 }, 86 "", 87 "pkg.B b;"); 88 } 89 90 void testPackageDirectAPI() throws Exception { 91 Path base = Paths.get("."); 92 Path libClasses = compileLib(base, 93 "package pkg;" + 94 "public class A {" + 95 "}", 96 "package pkg;" + 97 "public class B {" + 98 "}"); 99 100 Files.delete(libClasses.resolve("pkg/B.class")); 101 try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) { 102 out.write(0); 103 } 104 105 Path testSrc = base.resolve("test-src"); 106 tb.createDirectories(testSrc); 107 tb.writeJavaFiles(testSrc, 108 "package test;\n" + 109 "public class Test {\n" + 110 " void t() {\n" + 111 " pkg.B b;\n" + 112 " }\n" + 113 "}\n"); 114 Path testClasses = base.resolve("test-classes"); 115 tb.createDirectories(testClasses); 116 117 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 118 List<String> errors = new ArrayList<>(); 119 120 try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { 121 com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask) 122 compiler.getTask(null, 123 null, 124 d -> errors.add(d.getCode()), 125 Arrays.asList("-XDrawDiagnostics", 126 "-classpath", 127 libClasses.toString()), 128 null, 129 fm.getJavaFileObjects(tb.findJavaFiles(testSrc))); 130 task.parse(); 131 PackageElement pe = task.getElements().getPackageElement("pkg"); 132 for (Element el : pe.getEnclosedElements()) { 133 verifyElement(task, el); 134 } 135 task.analyze(); 136 } 137 138 List<String> expected = Arrays.asList("compiler.err.cant.access"); 139 140 if (!expected.equals(errors)) { 141 throw new IllegalStateException("Expected error not found!"); 142 } 143 } 144 145 void testSuperClass() throws Exception { 146 doTestCombo("class Test {" + 147 "}", 148 "package pkg;" + 149 "public class A extends # {" + 150 "}", 151 "pkg.A x;", 152 "# a = null; a.toString();", 153 (fqn, t) -> { 154 TypeElement a = t.getElements() 155 .getTypeElement(t.getElements() 156 .getModuleElement(""), 157 "pkg.A"); 158 TypeMirror superclass = a.getSuperclass(); 159 verifyTypeMirror(t, superclass); 160 assertEquals(TypeKind.DECLARED, superclass.getKind()); 161 Element superclassEl = ((DeclaredType) superclass).asElement(); 162 assertEquals(ElementKind.CLASS, superclassEl.getKind()); 163 assertEquals(TypeKind.ERROR, superclassEl.asType().getKind()); 164 }); 165 doTestCombo("interface Test {" + 166 "}", 167 "package pkg;" + 168 "public class A implements # {" + 169 "}", 170 "pkg.A x;", 171 "# a = null; a.toString();", 172 (fqn, t) -> { 173 TypeElement a = t.getElements().getTypeElement("pkg.A"); 174 TypeMirror superintf = a.getInterfaces().get(0); 175 verifyTypeMirror(t, superintf); 176 assertEquals(TypeKind.DECLARED, superintf.getKind()); 177 Element superintfEl = ((DeclaredType) superintf).asElement(); 178 //superintfEl.getKind() may be either CLASS or INTERFACE, depending on which class is missing 179 assertEquals(TypeKind.ERROR, superintfEl.asType().getKind()); 180 }); 181 doTestCombo("class Test {" + 182 "}", 183 "package pkg;" + 184 "public class A extends # {" + 185 "}", 186 "pkg.A x;", 187 "# a = null; a.toString();", 188 (fqn, t) -> { 189 TypeElement a = t.getElements() 190 .getTypeElement(t.getElements() 191 .getModuleElement(""), 192 "pkg.A"); 193 DeclaredType superclass = (DeclaredType) a.getSuperclass(); 194 superclass.getTypeArguments(); 195 }); 196 doTestCombo("class Test {" + 197 "}", 198 "package pkg;" + 199 "public class A extends # {" + 200 "}", 201 "pkg.A x;", 202 "# a = null; a.toString();", 203 (fqn, t) -> { 204 TypeElement a = t.getElements() 205 .getTypeElement(t.getElements() 206 .getModuleElement(""), 207 "pkg.A"); 208 DeclaredType superclass = (DeclaredType) a.getSuperclass(); 209 superclass.getEnclosingType(); 210 }); 211 } 212 213 void testAnnotation() throws Exception { 214 doTestCombo("@interface Test {" + 215 "}", 216 "package pkg;" + 217 "@#\n" + 218 "public class A {" + 219 "}", 220 "", 221 "# a = null; a.toString();", 222 (fqn, t) -> { 223 TypeElement a = t.getElements().getTypeElement("pkg.A"); 224 for (AnnotationMirror am : a.getAnnotationMirrors()) { 225 verifyTypeMirror(t, am.getAnnotationType()); 226 } 227 }); 228 doTestCombo("@interface Test {" + 229 " public Class<?> value();" + 230 "}", 231 "package pkg;" + 232 "@#(Object.class)\n" + 233 "public class A {" + 234 "}", 235 "", 236 "# a = null; a.toString();", 237 (fqn, t) -> { 238 TypeElement a = t.getElements().getTypeElement("pkg.A"); 239 for (AnnotationMirror am : a.getAnnotationMirrors()) { 240 verifyTypeMirror(t, am.getAnnotationType()); 241 if (am.getAnnotationType().toString().equals(fqn)) { 242 verifyTypeMirror(t, (TypeMirror) am.getElementValues().values() 243 .iterator().next().getValue()); 244 } 245 } 246 }); 247 doTestCombo("class Test { }", 248 "package pkg;" + 249 "@Ann(#.class)\n" + 250 "public class A {" + 251 "}" + 252 "@interface Ann {" + 253 " public Class<?> value();" + 254 "}", 255 "", 256 "# a = null; a.toString();", 257 (fqn, t) -> { 258 TypeElement a = t.getElements().getTypeElement("pkg.A"); 259 for (AnnotationMirror am : a.getAnnotationMirrors()) { 260 verifyTypeMirror(t, am.getAnnotationType()); 261 if (am.getAnnotationType().toString().equals(fqn)) { 262 verifyTypeMirror(t, (TypeMirror) am.getElementValues().values() 263 .iterator().next().getValue()); 264 } 265 } 266 }); 267 } 268 269 void testMethod() throws Exception { 270 doTestCombo("class Test {" + 271 "}", 272 "package pkg;" + 273 "public class A {" + 274 " public void m1(# t) { }" + 275 " public # m2() { return null; }" + 276 "}", 277 "", 278 "pkg.A a = null; a.m2().toString();", 279 (fqn, t) -> { 280 TypeElement a = t.getElements().getTypeElement("pkg.A"); 281 List<? extends Element> members = a.getEnclosedElements(); 282 if (members.size() != 3) 283 throw new AssertionError("Unexpected number of members, " + 284 "received members: " + members); 285 for (Element e : members) { 286 verifyElement(t, e); 287 } 288 }); 289 } 290 291 void testAnnotationProcessing() throws Exception { 292 boolean[] superClass = new boolean[1]; 293 class TestAP extends AbstractProcessor { 294 295 @Override 296 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 297 com.sun.source.util.JavacTask t = com.sun.source.util.JavacTask.instance(processingEnv); 298 TypeElement a = t.getElements().getTypeElement("pkg.A"); 299 if (superClass[0]) { 300 verifyTypeMirror(t, a.getSuperclass()); 301 } else { 302 verifyTypeMirror(t, a.getInterfaces().get(0)); 303 } 304 return false; 305 } 306 307 @Override 308 public Set<String> getSupportedAnnotationTypes() { 309 return Set.of("*"); 310 } 311 312 @Override 313 public SourceVersion getSupportedSourceVersion() { 314 return SourceVersion.latest(); 315 } 316 } 317 318 superClass[0] = true; 319 doTestComboCallBack("class Test {" + 320 "}", 321 "package pkg;" + 322 "public class A extends # {" + 323 "}", 324 "", 325 "# a = null; a.toString();", 326 (fqn, t) -> t.setProcessors(List.of(new TestAP()))); 327 superClass[0] = false; 328 doTestComboCallBack("interface Test {" + 329 "}", 330 "package pkg;" + 331 "public class A implements # {" + 332 "}", 333 "", 334 "# a = null; a.toString();", 335 (fqn, t) -> t.setProcessors(List.of(new TestAP()))); 336 } 337 338 void testGetTypeElement() throws Exception { 339 doTestCombo("class Test { }", 340 "package pkg;" + 341 "public class A extends # {" + 342 "}", 343 "", 344 "pkg.A a = null; a.toString();", //should be generalized/in variant? 345 (fqn, t) -> { 346 TypeElement a = t.getElements().getTypeElement(fqn); 347 if (a != null) { 348 throw new IllegalStateException(); 349 } 350 }); 351 } 352 353 private Path compileLib(Path base, String... sources) throws Exception { 354 Path libSrc = base.resolve("lib-src"); 355 tb.createDirectories(libSrc); 356 tb.writeJavaFiles(libSrc, sources); 357 Path libClasses = base.resolve("lib-classes"); 358 tb.createDirectories(libClasses); 359 new JavacTask(tb).outdir(libClasses.toString()) 360 .sourcepath(libSrc.toString()) 361 .files(tb.findJavaFiles(libSrc)) 362 .run() 363 .writeAll(); 364 365 return libClasses; 366 } 367 368 private void doTestCombo(String decl, 369 String use, 370 String snippetInClass, 371 String snippetInMethod, 372 BiConsumer<String, com.sun.source.util.JavacTask> test) throws Exception { 373 doTestComboCallBack(decl, 374 use, 375 snippetInClass, 376 snippetInMethod, 377 (fqn, t) -> { 378 t.addTaskListener(new TaskListener() { 379 @Override 380 public void finished(TaskEvent e) { 381 if (e.getKind() == TaskEvent.Kind.ENTER) { 382 test.accept(fqn, t); 383 } 384 } 385 }); 386 }); 387 } 388 389 private void doTestComboCallBack(String decl, 390 String use, 391 String snippetInClass, 392 String snippetInMethod, 393 BiConsumer<String, com.sun.source.util.JavacTask> callback) throws Exception { 394 List<TestVariant> variants = List.of( 395 new TestVariant("package pkg; public #", "pkg.Test", "pkg/Test.class"), 396 new TestVariant("package pkg; public class O { public static # }", "pkg.O.Test", "pkg/O$Test.class"), 397 new TestVariant("package pkg; public class O { public static # }", "pkg.O.Test", "pkg/O.class"), 398 new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O$N$Test.class"), 399 new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O$N.class"), 400 new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O.class") 401 ); 402 403 Path base = Paths.get("."); 404 405 for (TestVariant v : variants) { 406 System.err.println("-----------------------------------------------------------------------"); 407 System.err.println("variant: " + v.declarationStub + ", " + v.fqn + ", " + v.path); 408 Path libClasses = compileLib(base, 409 use.replace("#", v.fqn), 410 v.declarationStub.replace("#", decl)); 411 412 Files.delete(libClasses.resolve(v.path)); 413 414 doRunTestFullCallback(base, 415 t -> callback.accept(v.fqn, t), 416 snippetInClass.replace("#", v.fqn), 417 snippetInMethod.replace("#", v.fqn)); 418 } 419 } 420 421 private void doRunTest(Path base, 422 Consumer<com.sun.source.util.JavacTask> test, 423 String snippetInClass, 424 String snippetInMethod) throws Exception { 425 doRunTestFullCallback(base, t -> { 426 t.addTaskListener(new TaskListener() { 427 @Override 428 public void finished(TaskEvent e) { 429 if (e.getKind() == TaskEvent.Kind.ENTER) { 430 test.accept(t); 431 } 432 } 433 }); 434 }, snippetInClass, snippetInMethod); 435 } 436 437 private void doRunTestFullCallback(Path base, 438 Consumer<com.sun.source.util.JavacTask> callback, 439 String snippetInClass, 440 String snippetInMethod) throws Exception { 441 Path libClasses = base.resolve("lib-classes"); 442 Path testSrc = base.resolve("test-src"); 443 tb.createDirectories(testSrc); 444 tb.writeJavaFiles(testSrc, 445 "package test;\n" + 446 "public class Test {\n" + 447 snippetInClass + "\n" + 448 " void t() {\n" + 449 snippetInMethod + "\n" + 450 " }\n" + 451 "}\n"); 452 System.err.println("content: " + "package test;\n" + 453 "public class Test {\n" + 454 snippetInClass + "\n" + 455 " void t() {\n" + 456 snippetInMethod + "\n" + 457 " }\n" + 458 "}\n"); 459 Path testClasses = base.resolve("test-classes"); 460 tb.createDirectories(testClasses); 461 462 var expectedErrors = new JavacTask(tb).outdir(testClasses.toString()) 463 .options("-XDrawDiagnostics", 464 "-classpath", 465 libClasses.toString()) 466 .sourcepath(testSrc.toString()) 467 .files(tb.findJavaFiles(testSrc)) 468 .run(Expect.FAIL) 469 .writeAll() 470 .getOutputLines(OutputKind.DIRECT, 471 OutputKind.STDERR, 472 OutputKind.STDOUT); 473 474 var errors = new JavacTask(tb).outdir(testClasses.toString()) 475 .options("-XDrawDiagnostics", 476 "-classpath", 477 libClasses.toString()) 478 .sourcepath(testSrc.toString()) 479 .files(tb.findJavaFiles(testSrc)) 480 .callback(callback) 481 .run(Expect.FAIL) 482 .writeAll() 483 .getOutputLines(OutputKind.DIRECT, 484 OutputKind.STDERR, 485 OutputKind.STDOUT); 486 487 if (!expectedErrors.equals(errors)) { 488 throw new IllegalStateException("Expected error not found!"); 489 } 490 } 491 492 private void verifyTypeMirror(com.sun.source.util.JavacTask t, TypeMirror type) { 493 Element el = t.getTypes().asElement(type); 494 495 if (el != null) { 496 verifyElement(t, el); 497 } 498 } 499 500 private void verifyElement(com.sun.source.util.JavacTask t, Element el) { 501 el.getKind(); //forces completion 502 } 503 504 private static void assertEquals(Object expected, Object actual) { 505 if (!Objects.equals(expected, actual)) { 506 throw new AssertionError("Unexpected value, expected: " + expected + ", actual: " + actual); 507 } 508 } 509 510 public static void main(String... args) throws Exception { 511 MissingClassFile t = new MissingClassFile(); 512 t.testPackageContent(); 513 t.testPackageDirectAPI(); 514 t.testSuperClass(); 515 t.testAnnotation(); 516 t.testAnnotationProcessing(); 517 t.testGetTypeElement(); 518 } 519 520 static class TestVariant { 521 public final String declarationStub; 522 public final String fqn; 523 public final String path; 524 525 public TestVariant(String declarationStub, String fqn, String path) { 526 this.declarationStub = declarationStub; 527 this.fqn = fqn; 528 this.path = path; 529 } 530 531 } 532 }