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