TestClientCodeWrapper.java revision 4147:f260f1a2acf6
1/* 2 * Copyright (c) 2011, 2017, 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 6437138 6482554 27 * @summary JSR 199: Compiler doesn't diagnose crash in user code 28 * @library ../lib 29 * @modules jdk.compiler/com.sun.tools.javac.api 30 * @build JavacTestingAbstractProcessor TestClientCodeWrapper 31 * @run main TestClientCodeWrapper 32 */ 33 34import java.io.*; 35import java.lang.reflect.Method; 36import java.net.URI; 37import java.util.*; 38import javax.annotation.processing.*; 39import javax.lang.model.*; 40import javax.lang.model.element.*; 41import javax.tools.*; 42import javax.tools.JavaFileObject.Kind; 43 44import com.sun.source.util.*; 45import com.sun.tools.javac.api.*; 46 47public class TestClientCodeWrapper extends JavacTestingAbstractProcessor { 48 public static void main(String... args) throws Exception { 49 new TestClientCodeWrapper().run(); 50 } 51 52 /** 53 * Run a series of compilations, each with a different user-provided object 54 * configured to throw an exception when a specific method is invoked. 55 * Then, verify the exception is thrown as expected. 56 * 57 * Some methods are not invoked from the compiler, and are excluded from the test. 58 */ 59 void run() throws Exception { 60 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 61 try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { 62 defaultFileManager = fm; 63 64 for (Method m: getMethodsExcept(JavaFileManager.class, 65 "close", "getJavaFileForInput", "getLocationForModule", "getServiceLoader", "contains")) { 66 test(m); 67 } 68 69 for (Method m: getMethodsExcept(FileObject.class, "delete")) { 70 test(m); 71 } 72 73 for (Method m: getMethods(JavaFileObject.class)) { 74 test(m); 75 } 76 77 for (Method m: getMethodsExcept(Processor.class, "getCompletions")) { 78 test(m); 79 } 80 81 for (Method m: DiagnosticListener.class.getDeclaredMethods()) { 82 test(m); 83 } 84 85 for (Method m: TaskListener.class.getDeclaredMethods()) { 86 test(m); 87 } 88 89 if (errors > 0) 90 throw new Exception(errors + " errors occurred"); 91 } 92 } 93 94 /** Get a sorted set of the methods declared on a class. */ 95 Set<Method> getMethods(Class<?> clazz) { 96 return getMethodsExcept(clazz, new String[0]); 97 } 98 99 /** Get a sorted set of the methods declared on a class, excluding 100 * specified methods by name. */ 101 Set<Method> getMethodsExcept(Class<?> clazz, String... exclude) { 102 Set<Method> methods = new TreeSet<Method>(new Comparator<Method>() { 103 public int compare(Method m1, Method m2) { 104 return m1.toString().compareTo(m2.toString()); 105 } 106 }); 107 Set<String> e = new HashSet<String>(Arrays.asList(exclude)); 108 for (Method m: clazz.getDeclaredMethods()) { 109 if (!e.contains(m.getName())) 110 methods.add(m); 111 } 112 return methods; 113 } 114 115 /** 116 * Test a method in a user supplied component, to verify javac's handling 117 * of any exceptions thrown by that method. 118 */ 119 void test(Method m) throws Exception { 120 testNum++; 121 122 File extDirs = new File("empty-extdirs"); 123 extDirs.mkdirs(); 124 125 File testClasses = new File("test" + testNum); 126 testClasses.mkdirs(); 127 defaultFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(testClasses)); 128 129 System.err.println("test " + testNum + ": " 130 + m.getDeclaringClass().getSimpleName() + "." + m.getName()); 131 132 StringWriter sw = new StringWriter(); 133 PrintWriter pw = new PrintWriter(sw); 134 135 List<String> javacOptions = Arrays.asList( 136 "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", 137 "-extdirs", extDirs.getPath(), // for use by filemanager handleOption 138 "-processor", TestClientCodeWrapper.class.getName() 139 ); 140 141 List<String> classes = Collections.emptyList(); 142 143 JavacTool tool = JavacTool.create(); 144 try { 145 JavacTask task = tool.getTask(pw, 146 getFileManager(m, defaultFileManager), 147 getDiagnosticListener(m, pw), 148 javacOptions, 149 classes, 150 getCompilationUnits(m)); 151 152 if (isDeclaredIn(m, Processor.class)) 153 task.setProcessors(getProcessors(m)); 154 155 if (isDeclaredIn(m, TaskListener.class)) 156 task.setTaskListener(getTaskListener(m, pw)); 157 158 boolean ok = task.call(); 159 error("compilation " + (ok ? "succeeded" : "failed") + " unexpectedly"); 160 } catch (RuntimeException e) { 161 System.err.println("caught " + e); 162 if (e.getClass() == RuntimeException.class) { 163 Throwable cause = e.getCause(); 164 if (cause instanceof UserError) { 165 String expect = m.getName(); 166 String found = cause.getMessage(); 167 checkEqual("exception messaqe", expect, found); 168 } else { 169 cause.printStackTrace(System.err); 170 error("Unexpected exception: " + cause); 171 } 172 } else { 173 e.printStackTrace(System.err); 174 error("Unexpected exception: " + e); 175 } 176 } 177 178 pw.close(); 179 String out = sw.toString(); 180 System.err.println(out); 181 } 182 183 /** Get a file manager to use for the test compilation. */ 184 JavaFileManager getFileManager(Method m, JavaFileManager defaultFileManager) { 185 return isDeclaredIn(m, JavaFileManager.class, FileObject.class, JavaFileObject.class) 186 ? new UserFileManager(m, defaultFileManager) 187 : defaultFileManager; 188 } 189 190 /** Get a diagnostic listener to use for the test compilation. */ 191 DiagnosticListener<JavaFileObject> getDiagnosticListener(Method m, PrintWriter out) { 192 return isDeclaredIn(m, DiagnosticListener.class) 193 ? new UserDiagnosticListener(m, out) 194 : null; 195 } 196 197 /** Get a set of file objects to use for the test compilation. */ 198 Iterable<? extends JavaFileObject> getCompilationUnits(Method m) { 199 File testSrc = new File(System.getProperty("test.src")); 200 File thisSrc = new File(testSrc, TestClientCodeWrapper.class.getName() + ".java"); 201 Iterable<? extends JavaFileObject> files = defaultFileManager.getJavaFileObjects(thisSrc); 202 if (isDeclaredIn(m, FileObject.class, JavaFileObject.class)) 203 return Arrays.asList(new UserFileObject(m, files.iterator().next())); 204 else 205 return files; 206 } 207 208 /** Get a set of annotation processors to use for the test compilation. */ 209 Iterable<? extends Processor> getProcessors(Method m) { 210 return Arrays.asList(new UserProcessor(m)); 211 } 212 213 /** Get a task listener to use for the test compilation. */ 214 TaskListener getTaskListener(Method m, PrintWriter out) { 215 return new UserTaskListener(m, out); 216 } 217 218 /** Check if two values are .equal, and report an error if not. */ 219 <T> void checkEqual(String label, T expect, T found) { 220 if (!expect.equals(found)) 221 error("Unexpected value for " + label + ": " + found + "; expected: " + expect); 222 } 223 224 /** Report an error. */ 225 void error(String msg) { 226 System.err.println("Error: " + msg); 227 errors++; 228 } 229 230 /** Check if a method is declared in any of a set of classes */ 231 static boolean isDeclaredIn(Method m, Class<?>... classes) { 232 Class<?> dc = m.getDeclaringClass(); 233 for (Class<?> c: classes) { 234 if (c == dc) return true; 235 } 236 return false; 237 } 238 239 /** Throw an intentional error if the method has a given name. */ 240 static void throwUserExceptionIfNeeded(Method m, String name) { 241 if (m != null && m.getName().equals(name)) 242 throw new UserError(name); 243 } 244 245 StandardJavaFileManager defaultFileManager; 246 int testNum; 247 int errors; 248 249 //-------------------------------------------------------------------------- 250 251 /** 252 * Processor used to trigger use of methods not normally used by javac. 253 */ 254 @Override 255 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 256 boolean firstRound = false; 257 for (Element e: roundEnv.getRootElements()) { 258 if (e.getSimpleName().contentEquals(TestClientCodeWrapper.class.getSimpleName())) 259 firstRound = true; 260 } 261 if (firstRound) { 262 try { 263 FileObject f1 = filer.getResource(StandardLocation.CLASS_PATH, "", 264 TestClientCodeWrapper.class.getName() + ".java"); 265 f1.openInputStream().close(); 266 f1.openReader(false).close(); 267 268 FileObject f2 = filer.createResource( 269 StandardLocation.CLASS_OUTPUT, "", "f2.txt", (Element[]) null); 270 f2.openOutputStream().close(); 271 272 FileObject f3 = filer.createResource( 273 StandardLocation.CLASS_OUTPUT, "", "f3.txt", (Element[]) null); 274 f3.openWriter().close(); 275 276 JavaFileObject f4 = filer.createSourceFile("f4", (Element[]) null); 277 f4.openWriter().close(); 278 f4.getNestingKind(); 279 f4.getAccessLevel(); 280 281 messager.printMessage(Diagnostic.Kind.NOTE, "informational note", 282 roundEnv.getRootElements().iterator().next()); 283 284 } catch (IOException e) { 285 throw new UserError(e); 286 } 287 } 288 return true; 289 } 290 291 //-------------------------------------------------------------------------- 292 293 // <editor-fold defaultstate="collapsed" desc="User classes"> 294 295 static class UserError extends Error { 296 private static final long serialVersionUID = 1L; 297 UserError(String msg) { 298 super(msg); 299 } 300 UserError(Throwable t) { 301 super(t); 302 } 303 } 304 305 static class UserFileManager extends ForwardingJavaFileManager<JavaFileManager> { 306 Method fileManagerMethod; 307 Method fileObjectMethod; 308 309 UserFileManager(Method m, JavaFileManager delegate) { 310 super(delegate); 311 if (isDeclaredIn(m, JavaFileManager.class)) { 312 fileManagerMethod = m; 313 } else if (isDeclaredIn(m, FileObject.class, JavaFileObject.class)) { 314 fileObjectMethod = m; 315 } else 316 assert false; 317 } 318 319 @Override 320 public ClassLoader getClassLoader(Location location) { 321 throwUserExceptionIfNeeded(fileManagerMethod, "getClassLoader"); 322 return super.getClassLoader(location); 323 } 324 325 @Override 326 public <S> ServiceLoader getServiceLoader(Location location, Class<S> service) throws IOException { 327 throwUserExceptionIfNeeded(fileManagerMethod, "getServiceLoader"); 328 return super.getServiceLoader(location, service); 329 } 330 331 @Override 332 public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException { 333 throwUserExceptionIfNeeded(fileManagerMethod, "list"); 334 return wrap(super.list(location, packageName, kinds, recurse)); 335 } 336 337 @Override 338 public String inferBinaryName(Location location, JavaFileObject file) { 339 throwUserExceptionIfNeeded(fileManagerMethod, "inferBinaryName"); 340 return super.inferBinaryName(location, unwrap(file)); 341 } 342 343 @Override 344 public boolean isSameFile(FileObject a, FileObject b) { 345 throwUserExceptionIfNeeded(fileManagerMethod, "isSameFile"); 346 return super.isSameFile(unwrap(a), unwrap(b)); 347 } 348 349 @Override 350 public boolean handleOption(String current, Iterator<String> remaining) { 351 throwUserExceptionIfNeeded(fileManagerMethod, "handleOption"); 352 return super.handleOption(current, remaining); 353 } 354 355 @Override 356 public boolean hasLocation(Location location) { 357 throwUserExceptionIfNeeded(fileManagerMethod, "hasLocation"); 358 return super.hasLocation(location); 359 } 360 361 @Override 362 public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException { 363 throwUserExceptionIfNeeded(fileManagerMethod, "getJavaFileForInput"); 364 return wrap(super.getJavaFileForInput(location, className, kind)); 365 } 366 367 @Override 368 public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { 369 throwUserExceptionIfNeeded(fileManagerMethod, "getJavaFileForOutput"); 370 return wrap(super.getJavaFileForOutput(location, className, kind, sibling)); 371 } 372 373 @Override 374 public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { 375 throwUserExceptionIfNeeded(fileManagerMethod, "getFileForInput"); 376 return wrap(super.getFileForInput(location, packageName, relativeName)); 377 } 378 379 @Override 380 public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { 381 throwUserExceptionIfNeeded(fileManagerMethod, "getFileForOutput"); 382 return wrap(super.getFileForOutput(location, packageName, relativeName, sibling)); 383 } 384 385 @Override 386 public void flush() throws IOException { 387 throwUserExceptionIfNeeded(fileManagerMethod, "flush"); 388 super.flush(); 389 } 390 391 @Override 392 public void close() throws IOException { 393 throwUserExceptionIfNeeded(fileManagerMethod, "close"); 394 super.close(); 395 } 396 397 @Override 398 public int isSupportedOption(String option) { 399 throwUserExceptionIfNeeded(fileManagerMethod, "isSupportedOption"); 400 return super.isSupportedOption(option); 401 } 402 403 @Override 404 public Location getLocationForModule(Location location, String moduleName) throws IOException { 405 throwUserExceptionIfNeeded(fileManagerMethod, "getLocationForModule"); 406 return super.getLocationForModule(location, moduleName); 407 } 408 409 @Override 410 public Location getLocationForModule(Location location, JavaFileObject fo) throws IOException { 411 throwUserExceptionIfNeeded(fileManagerMethod, "getLocationForModule"); 412 return super.getLocationForModule(location, fo); 413 } 414 415 @Override 416 public String inferModuleName(Location location) throws IOException { 417 throwUserExceptionIfNeeded(fileManagerMethod, "inferModuleName"); 418 return super.inferModuleName(location); 419 } 420 421 @Override 422 public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException { 423 throwUserExceptionIfNeeded(fileManagerMethod, "listLocationsForModules"); 424 return super.listLocationsForModules(location); 425 } 426 427 @Override 428 public boolean contains(Location location, FileObject fo) throws IOException { 429 throwUserExceptionIfNeeded(fileManagerMethod, "contains"); 430 return super.contains(location, fo); 431 } 432 433 public FileObject wrap(FileObject fo) { 434 if (fileObjectMethod == null || fo == null) 435 return fo; 436 return new UserFileObject(fileObjectMethod, (JavaFileObject)fo); 437 } 438 439 FileObject unwrap(FileObject fo) { 440 if (fo instanceof UserFileObject) 441 return ((UserFileObject) fo).unwrap(); 442 else 443 return fo; 444 } 445 446 public JavaFileObject wrap(JavaFileObject fo) { 447 if (fileObjectMethod == null || fo == null) 448 return fo; 449 return new UserFileObject(fileObjectMethod, fo); 450 } 451 452 public Iterable<JavaFileObject> wrap(Iterable<? extends JavaFileObject> list) { 453 List<JavaFileObject> wrapped = new ArrayList<JavaFileObject>(); 454 for (JavaFileObject fo : list) 455 wrapped.add(wrap(fo)); 456 return Collections.unmodifiableList(wrapped); 457 } 458 459 JavaFileObject unwrap(JavaFileObject fo) { 460 if (fo instanceof UserFileObject) 461 return ((UserFileObject) fo).unwrap(); 462 else 463 return fo; 464 } 465 } 466 467 static class UserFileObject extends ForwardingJavaFileObject<JavaFileObject> { 468 Method method; 469 470 UserFileObject(Method m, JavaFileObject delegate) { 471 super(delegate); 472 assert isDeclaredIn(m, FileObject.class, JavaFileObject.class); 473 this.method = m; 474 } 475 476 JavaFileObject unwrap() { 477 return fileObject; 478 } 479 480 @Override 481 public Kind getKind() { 482 throwUserExceptionIfNeeded(method, "getKind"); 483 return super.getKind(); 484 } 485 486 @Override 487 public boolean isNameCompatible(String simpleName, Kind kind) { 488 throwUserExceptionIfNeeded(method, "isNameCompatible"); 489 return super.isNameCompatible(simpleName, kind); 490 } 491 492 @Override 493 public NestingKind getNestingKind() { 494 throwUserExceptionIfNeeded(method, "getNestingKind"); 495 return super.getNestingKind(); 496 } 497 498 @Override 499 public Modifier getAccessLevel() { 500 throwUserExceptionIfNeeded(method, "getAccessLevel"); 501 return super.getAccessLevel(); 502 } 503 504 @Override 505 public URI toUri() { 506 throwUserExceptionIfNeeded(method, "toUri"); 507 return super.toUri(); 508 } 509 510 @Override 511 public String getName() { 512 throwUserExceptionIfNeeded(method, "getName"); 513 return super.getName(); 514 } 515 516 @Override 517 public InputStream openInputStream() throws IOException { 518 throwUserExceptionIfNeeded(method, "openInputStream"); 519 return super.openInputStream(); 520 } 521 522 @Override 523 public OutputStream openOutputStream() throws IOException { 524 throwUserExceptionIfNeeded(method, "openOutputStream"); 525 return super.openOutputStream(); 526 } 527 528 @Override 529 public Reader openReader(boolean ignoreEncodingErrors) throws IOException { 530 throwUserExceptionIfNeeded(method, "openReader"); 531 return super.openReader(ignoreEncodingErrors); 532 } 533 534 @Override 535 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 536 throwUserExceptionIfNeeded(method, "getCharContent"); 537 return super.getCharContent(ignoreEncodingErrors); 538 } 539 540 @Override 541 public Writer openWriter() throws IOException { 542 throwUserExceptionIfNeeded(method, "openWriter"); 543 return super.openWriter(); 544 } 545 546 @Override 547 public long getLastModified() { 548 throwUserExceptionIfNeeded(method, "getLastModified"); 549 return super.getLastModified(); 550 } 551 552 @Override 553 public boolean delete() { 554 throwUserExceptionIfNeeded(method, "delete"); 555 return super.delete(); 556 } 557 558 } 559 560 static class UserProcessor extends JavacTestingAbstractProcessor { 561 Method method; 562 563 UserProcessor(Method m) { 564 assert isDeclaredIn(m, Processor.class); 565 method = m; 566 } 567 568 @Override 569 public Set<String> getSupportedOptions() { 570 throwUserExceptionIfNeeded(method, "getSupportedOptions"); 571 return super.getSupportedOptions(); 572 } 573 574 @Override 575 public Set<String> getSupportedAnnotationTypes() { 576 throwUserExceptionIfNeeded(method, "getSupportedAnnotationTypes"); 577 return super.getSupportedAnnotationTypes(); 578 } 579 580 @Override 581 public SourceVersion getSupportedSourceVersion() { 582 throwUserExceptionIfNeeded(method, "getSupportedSourceVersion"); 583 return super.getSupportedSourceVersion(); 584 } 585 586 @Override 587 public void init(ProcessingEnvironment processingEnv) { 588 throwUserExceptionIfNeeded(method, "init"); 589 super.init(processingEnv); 590 } 591 592 @Override 593 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 594 throwUserExceptionIfNeeded(method, "process"); 595 return true; 596 } 597 598 @Override 599 public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) { 600 throwUserExceptionIfNeeded(method, "getCompletions"); 601 return super.getCompletions(element, annotation, member, userText); 602 } 603 } 604 605 static class UserDiagnosticListener implements DiagnosticListener<JavaFileObject> { 606 Method method; 607 PrintWriter out; 608 609 UserDiagnosticListener(Method m, PrintWriter out) { 610 assert isDeclaredIn(m, DiagnosticListener.class); 611 this.method = m; 612 this.out = out; 613 } 614 615 @Override 616 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 617 throwUserExceptionIfNeeded(method, "report"); 618 out.println("report: " + diagnostic); 619 } 620 } 621 622 static class UserTaskListener implements TaskListener { 623 Method method; 624 PrintWriter out; 625 626 UserTaskListener(Method m, PrintWriter out) { 627 assert isDeclaredIn(m, TaskListener.class); 628 this.method = m; 629 this.out = out; 630 } 631 632 @Override 633 public void started(TaskEvent e) { 634 throwUserExceptionIfNeeded(method, "started"); 635 out.println("started: " + e); 636 } 637 638 @Override 639 public void finished(TaskEvent e) { 640 throwUserExceptionIfNeeded(method, "finished"); 641 out.println("finished: " + e); 642 } 643 } 644 645 // </editor-fold> 646} 647