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