1 /* 2 * Copyright 2005-2009 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package com.sun.tools.javac.processing; 27 28 import java.lang.reflect.*; 29 import java.util.*; 30 import java.util.regex.*; 31 32 import java.net.URL; 33 import java.io.Closeable; 34 import java.io.File; 35 import java.io.PrintWriter; 36 import java.io.IOException; 37 import java.net.MalformedURLException; 38 import java.io.StringWriter; 39 40 import javax.annotation.processing.*; 41 import javax.lang.model.SourceVersion; 42 import javax.lang.model.element.AnnotationMirror; 43 import javax.lang.model.element.Element; 44 import javax.lang.model.element.TypeElement; 45 import javax.lang.model.element.PackageElement; 46 import javax.lang.model.util.*; 47 import javax.tools.JavaFileManager; 48 import javax.tools.StandardJavaFileManager; 49 import javax.tools.JavaFileObject; 50 import javax.tools.DiagnosticListener; 51 52 import com.sun.source.util.AbstractTypeProcessor; 53 import com.sun.source.util.TaskEvent; 54 import com.sun.source.util.TaskListener; 55 import com.sun.tools.javac.api.JavacTaskImpl; 56 import com.sun.tools.javac.code.*; 57 import com.sun.tools.javac.code.Symbol.*; 58 import com.sun.tools.javac.file.JavacFileManager; 59 import com.sun.tools.javac.jvm.*; 60 import com.sun.tools.javac.main.JavaCompiler; 61 import com.sun.tools.javac.main.JavaCompiler.CompileState; 62 import com.sun.tools.javac.model.JavacElements; 63 import com.sun.tools.javac.model.JavacTypes; 64 import com.sun.tools.javac.parser.*; 65 import com.sun.tools.javac.tree.*; 66 import com.sun.tools.javac.tree.JCTree.*; 67 import com.sun.tools.javac.util.Abort; 68 import com.sun.tools.javac.util.Context; 69 import com.sun.tools.javac.util.Convert; 70 import com.sun.tools.javac.util.List; 71 import com.sun.tools.javac.util.ListBuffer; 72 import com.sun.tools.javac.util.Log; 73 import com.sun.tools.javac.util.JavacMessages; 74 import com.sun.tools.javac.util.Name; 75 import com.sun.tools.javac.util.Names; 76 import com.sun.tools.javac.util.Options; 77 78 import static javax.tools.StandardLocation.*; 79 80 /** 81 * Objects of this class hold and manage the state needed to support 82 * annotation processing. 83 * 84 * <p><b>This is NOT part of any API supported by Sun Microsystems. 85 * If you write code that depends on this, you do so at your own risk. 86 * This code and its internal interfaces are subject to change or 87 * deletion without notice.</b> 88 */ 89 public class JavacProcessingEnvironment implements ProcessingEnvironment, Closeable { 90 Options options; 91 92 private final boolean printProcessorInfo; 93 private final boolean printRounds; 94 private final boolean verbose; 95 private final boolean lint; 96 private final boolean procOnly; 97 private final boolean fatalErrors; 98 private boolean foundTypeProcessors; 99 100 private final JavacFiler filer; 101 private final JavacMessager messager; 102 private final JavacElements elementUtils; 103 private final JavacTypes typeUtils; 104 105 /** 106 * Holds relevant state history of which processors have been 107 * used. 108 */ 109 private DiscoveredProcessors discoveredProcs; 110 111 /** 112 * Map of processor-specific options. 113 */ 114 private final Map<String, String> processorOptions; 115 116 /** 117 */ 118 private final Set<String> unmatchedProcessorOptions; 119 120 /** 121 * Annotations implicitly processed and claimed by javac. 122 */ 123 private final Set<String> platformAnnotations; 124 125 /** 126 * Set of packages given on command line. 127 */ 128 private Set<PackageSymbol> specifiedPackages = Collections.emptySet(); 129 130 /** The log to be used for error reporting. 131 */ 132 Log log; 133 134 /** 135 * Source level of the compile. 136 */ 137 Source source; 138 139 private ClassLoader processorClassLoader; 140 141 /** 142 * JavacMessages object used for localization 143 */ 144 private JavacMessages messages; 145 146 private Context context; 147 148 public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) { 149 options = Options.instance(context); 150 this.context = context; 151 log = Log.instance(context); 152 source = Source.instance(context); 153 printProcessorInfo = options.get("-XprintProcessorInfo") != null; 154 printRounds = options.get("-XprintRounds") != null; 155 verbose = options.get("-verbose") != null; 156 lint = options.lint("processing"); 157 procOnly = options.get("-proc:only") != null || 158 options.get("-Xprint") != null; 159 fatalErrors = options.get("fatalEnterError") != null; 160 platformAnnotations = initPlatformAnnotations(); 161 foundTypeProcessors = false; 162 163 // Initialize services before any processors are initialzied 164 // in case processors use them. 165 filer = new JavacFiler(context); 166 messager = new JavacMessager(context, this); 167 elementUtils = new JavacElements(context); 168 typeUtils = new JavacTypes(context); 169 processorOptions = initProcessorOptions(context); 170 unmatchedProcessorOptions = initUnmatchedProcessorOptions(); 171 messages = JavacMessages.instance(context); 172 initProcessorIterator(context, processors); 173 } 174 175 private Set<String> initPlatformAnnotations() { 176 Set<String> platformAnnotations = new HashSet<String>(); 177 platformAnnotations.add("java.lang.Deprecated"); 178 platformAnnotations.add("java.lang.Override"); 179 platformAnnotations.add("java.lang.SuppressWarnings"); 180 platformAnnotations.add("java.lang.annotation.Documented"); 181 platformAnnotations.add("java.lang.annotation.Inherited"); 182 platformAnnotations.add("java.lang.annotation.Retention"); 183 platformAnnotations.add("java.lang.annotation.Target"); 184 return Collections.unmodifiableSet(platformAnnotations); 185 } 186 187 private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) { 188 Log log = Log.instance(context); 189 Iterator<? extends Processor> processorIterator; 190 191 if (options.get("-Xprint") != null) { 192 try { 193 Processor processor = PrintingProcessor.class.newInstance(); 194 processorIterator = List.of(processor).iterator(); 195 } catch (Throwable t) { 196 AssertionError assertError = 197 new AssertionError("Problem instantiating PrintingProcessor."); 198 assertError.initCause(t); 199 throw assertError; 200 } 201 } else if (processors != null) { 202 processorIterator = processors.iterator(); 203 } else { 204 String processorNames = options.get("-processor"); 205 JavaFileManager fileManager = context.get(JavaFileManager.class); 206 try { 207 // If processorpath is not explicitly set, use the classpath. 208 processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH) 209 ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH) 210 : fileManager.getClassLoader(CLASS_PATH); 211 212 /* 213 * If the "-processor" option is used, search the appropriate 214 * path for the named class. Otherwise, use a service 215 * provider mechanism to create the processor iterator. 216 */ 217 if (processorNames != null) { 218 processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log); 219 } else { 220 processorIterator = new ServiceIterator(processorClassLoader, log); 221 } 222 } catch (SecurityException e) { 223 /* 224 * A security exception will occur if we can't create a classloader. 225 * Ignore the exception if, with hindsight, we didn't need it anyway 226 * (i.e. no processor was specified either explicitly, or implicitly, 227 * in service configuration file.) Otherwise, we cannot continue. 228 */ 229 processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e); 230 } 231 } 232 discoveredProcs = new DiscoveredProcessors(processorIterator); 233 } 234 235 /** 236 * Returns an empty processor iterator if no processors are on the 237 * relevant path, otherwise if processors are present, logs an 238 * error. Called when a service loader is unavailable for some 239 * reason, either because a service loader class cannot be found 240 * or because a security policy prevents class loaders from being 241 * created. 242 * 243 * @param key The resource key to use to log an error message 244 * @param e If non-null, pass this exception to Abort 245 */ 246 private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) { 247 JavaFileManager fileManager = context.get(JavaFileManager.class); 248 249 if (fileManager instanceof JavacFileManager) { 250 StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager; 251 Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH) 252 ? standardFileManager.getLocation(ANNOTATION_PROCESSOR_PATH) 253 : standardFileManager.getLocation(CLASS_PATH); 254 255 if (needClassLoader(options.get("-processor"), workingPath) ) 256 handleException(key, e); 257 258 } else { 259 handleException(key, e); 260 } 261 262 java.util.List<Processor> pl = Collections.emptyList(); 263 return pl.iterator(); 264 } 265 266 /** 267 * Handle a security exception thrown during initializing the 268 * Processor iterator. 269 */ 270 private void handleException(String key, Exception e) { 271 if (e != null) { 272 log.error(key, e.getLocalizedMessage()); 273 throw new Abort(e); 274 } else { 275 log.error(key); 276 throw new Abort(); 277 } 278 } 279 280 /** 281 * Use a service loader appropriate for the platform to provide an 282 * iterator over annotations processors. If 283 * java.util.ServiceLoader is present use it, otherwise, use 284 * sun.misc.Service, otherwise fail if a loader is needed. 285 */ 286 private class ServiceIterator implements Iterator<Processor> { 287 // The to-be-wrapped iterator. 288 private Iterator<?> iterator; 289 private Log log; 290 private Class<?> loaderClass; 291 private boolean jusl; 292 private Object loader; 293 294 ServiceIterator(ClassLoader classLoader, Log log) { 295 String loadMethodName; 296 297 this.log = log; 298 try { 299 try { 300 loaderClass = Class.forName("java.util.ServiceLoader"); 301 loadMethodName = "load"; 302 jusl = true; 303 } catch (ClassNotFoundException cnfe) { 304 try { 305 loaderClass = Class.forName("sun.misc.Service"); 306 loadMethodName = "providers"; 307 jusl = false; 308 } catch (ClassNotFoundException cnfe2) { 309 // Fail softly if a loader is not actually needed. 310 this.iterator = handleServiceLoaderUnavailability("proc.no.service", 311 null); 312 return; 313 } 314 } 315 316 // java.util.ServiceLoader.load or sun.misc.Service.providers 317 Method loadMethod = loaderClass.getMethod(loadMethodName, 318 Class.class, 319 ClassLoader.class); 320 321 Object result = loadMethod.invoke(null, 322 Processor.class, 323 classLoader); 324 325 // For java.util.ServiceLoader, we have to call another 326 // method to get the iterator. 327 if (jusl) { 328 loader = result; // Store ServiceLoader to call reload later 329 Method m = loaderClass.getMethod("iterator"); 330 result = m.invoke(result); // serviceLoader.iterator(); 331 } 332 333 // The result should now be an iterator. 334 this.iterator = (Iterator<?>) result; 335 } catch (Throwable t) { 336 log.error("proc.service.problem"); 337 throw new Abort(t); 338 } 339 } 340 341 public boolean hasNext() { 342 try { 343 return iterator.hasNext(); 344 } catch (Throwable t) { 345 if ("ServiceConfigurationError". 346 equals(t.getClass().getSimpleName())) { 347 log.error("proc.bad.config.file", t.getLocalizedMessage()); 348 } 349 throw new Abort(t); 350 } 351 } 352 353 public Processor next() { 354 try { 355 return (Processor)(iterator.next()); 356 } catch (Throwable t) { 357 if ("ServiceConfigurationError". 358 equals(t.getClass().getSimpleName())) { 359 log.error("proc.bad.config.file", t.getLocalizedMessage()); 360 } else { 361 log.error("proc.processor.constructor.error", t.getLocalizedMessage()); 362 } 363 throw new Abort(t); 364 } 365 } 366 367 public void remove() { 368 throw new UnsupportedOperationException(); 369 } 370 371 public void close() { 372 if (jusl) { 373 try { 374 // Call java.util.ServiceLoader.reload 375 Method reloadMethod = loaderClass.getMethod("reload"); 376 reloadMethod.invoke(loader); 377 } catch(Exception e) { 378 ; // Ignore problems during a call to reload. 379 } 380 } 381 } 382 } 383 384 385 private static class NameProcessIterator implements Iterator<Processor> { 386 Processor nextProc = null; 387 Iterator<String> names; 388 ClassLoader processorCL; 389 Log log; 390 391 NameProcessIterator(String names, ClassLoader processorCL, Log log) { 392 this.names = Arrays.asList(names.split(",")).iterator(); 393 this.processorCL = processorCL; 394 this.log = log; 395 } 396 397 public boolean hasNext() { 398 if (nextProc != null) 399 return true; 400 else { 401 if (!names.hasNext()) 402 return false; 403 else { 404 String processorName = names.next(); 405 406 Processor processor; 407 try { 408 try { 409 processor = 410 (Processor) (processorCL.loadClass(processorName).newInstance()); 411 } catch (ClassNotFoundException cnfe) { 412 log.error("proc.processor.not.found", processorName); 413 return false; 414 } catch (ClassCastException cce) { 415 log.error("proc.processor.wrong.type", processorName); 416 return false; 417 } catch (Exception e ) { 418 log.error("proc.processor.cant.instantiate", processorName); 419 return false; 420 } 421 } catch(Throwable t) { 422 throw new AnnotationProcessingError(t); 423 } 424 nextProc = processor; 425 return true; 426 } 427 428 } 429 } 430 431 public Processor next() { 432 if (hasNext()) { 433 Processor p = nextProc; 434 nextProc = null; 435 return p; 436 } else 437 throw new NoSuchElementException(); 438 } 439 440 public void remove () { 441 throw new UnsupportedOperationException(); 442 } 443 } 444 445 public boolean atLeastOneProcessor() { 446 return discoveredProcs.iterator().hasNext(); 447 } 448 449 private Map<String, String> initProcessorOptions(Context context) { 450 Options options = Options.instance(context); 451 Set<String> keySet = options.keySet(); 452 Map<String, String> tempOptions = new LinkedHashMap<String, String>(); 453 454 for(String key : keySet) { 455 if (key.startsWith("-A") && key.length() > 2) { 456 int sepIndex = key.indexOf('='); 457 String candidateKey = null; 458 String candidateValue = null; 459 460 if (sepIndex == -1) 461 candidateKey = key.substring(2); 462 else if (sepIndex >= 3) { 463 candidateKey = key.substring(2, sepIndex); 464 candidateValue = (sepIndex < key.length()-1)? 465 key.substring(sepIndex+1) : null; 466 } 467 tempOptions.put(candidateKey, candidateValue); 468 } 469 } 470 471 return Collections.unmodifiableMap(tempOptions); 472 } 473 474 private Set<String> initUnmatchedProcessorOptions() { 475 Set<String> unmatchedProcessorOptions = new HashSet<String>(); 476 unmatchedProcessorOptions.addAll(processorOptions.keySet()); 477 return unmatchedProcessorOptions; 478 } 479 480 /** 481 * State about how a processor has been used by the tool. If a 482 * processor has been used on a prior round, its process method is 483 * called on all subsequent rounds, perhaps with an empty set of 484 * annotations to process. The {@code annotatedSupported} method 485 * caches the supported annotation information from the first (and 486 * only) getSupportedAnnotationTypes call to the processor. 487 */ 488 static class ProcessorState { 489 public Processor processor; 490 public boolean contributed; 491 private ArrayList<Pattern> supportedAnnotationPatterns; 492 private ArrayList<String> supportedOptionNames; 493 494 ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) { 495 processor = p; 496 contributed = false; 497 498 try { 499 processor.init(env); 500 501 checkSourceVersionCompatibility(source, log); 502 503 supportedAnnotationPatterns = new ArrayList<Pattern>(); 504 for (String importString : processor.getSupportedAnnotationTypes()) { 505 supportedAnnotationPatterns.add(importStringToPattern(importString, 506 processor, 507 log)); 508 } 509 510 supportedOptionNames = new ArrayList<String>(); 511 for (String optionName : processor.getSupportedOptions() ) { 512 if (checkOptionName(optionName, log)) 513 supportedOptionNames.add(optionName); 514 } 515 516 } catch (Throwable t) { 517 throw new AnnotationProcessingError(t); 518 } 519 } 520 521 /** 522 * Checks whether or not a processor's source version is 523 * compatible with the compilation source version. The 524 * processor's source version needs to be greater than or 525 * equal to the source version of the compile. 526 */ 527 private void checkSourceVersionCompatibility(Source source, Log log) { 528 SourceVersion procSourceVersion = processor.getSupportedSourceVersion(); 529 530 if (procSourceVersion.compareTo(Source.toSourceVersion(source)) < 0 ) { 531 log.warning("proc.processor.incompatible.source.version", 532 procSourceVersion, 533 processor.getClass().getName(), 534 source.name); 535 } 536 } 537 538 private boolean checkOptionName(String optionName, Log log) { 539 boolean valid = isValidOptionName(optionName); 540 if (!valid) 541 log.error("proc.processor.bad.option.name", 542 optionName, 543 processor.getClass().getName()); 544 return valid; 545 } 546 547 public boolean annotationSupported(String annotationName) { 548 for(Pattern p: supportedAnnotationPatterns) { 549 if (p.matcher(annotationName).matches()) 550 return true; 551 } 552 return false; 553 } 554 555 /** 556 * Remove options that are matched by this processor. 557 */ 558 public void removeSupportedOptions(Set<String> unmatchedProcessorOptions) { 559 unmatchedProcessorOptions.removeAll(supportedOptionNames); 560 } 561 } 562 563 // TODO: These two classes can probably be rewritten better... 564 /** 565 * This class holds information about the processors that have 566 * been discoverd so far as well as the means to discover more, if 567 * necessary. A single iterator should be used per round of 568 * annotation processing. The iterator first visits already 569 * discovered processors then fails over to the service provider 570 * mechanism if additional queries are made. 571 */ 572 class DiscoveredProcessors implements Iterable<ProcessorState> { 573 574 class ProcessorStateIterator implements Iterator<ProcessorState> { 575 DiscoveredProcessors psi; 576 Iterator<ProcessorState> innerIter; 577 boolean onProcInterator; 578 579 ProcessorStateIterator(DiscoveredProcessors psi) { 580 this.psi = psi; 581 this.innerIter = psi.procStateList.iterator(); 582 this.onProcInterator = false; 583 } 584 585 public ProcessorState next() { 586 if (!onProcInterator) { 587 if (innerIter.hasNext()) 588 return innerIter.next(); 589 else 590 onProcInterator = true; 591 } 592 593 if (psi.processorIterator.hasNext()) { 594 ProcessorState ps = new ProcessorState(psi.processorIterator.next(), 595 log, source, JavacProcessingEnvironment.this); 596 psi.procStateList.add(ps); 597 return ps; 598 } else 599 throw new NoSuchElementException(); 600 } 601 602 public boolean hasNext() { 603 if (onProcInterator) 604 return psi.processorIterator.hasNext(); 605 else 606 return innerIter.hasNext() || psi.processorIterator.hasNext(); 607 } 608 609 public void remove () { 610 throw new UnsupportedOperationException(); 611 } 612 613 /** 614 * Run all remaining processors on the procStateList that 615 * have not already run this round with an empty set of 616 * annotations. 617 */ 618 public void runContributingProcs(RoundEnvironment re) { 619 if (!onProcInterator) { 620 Set<TypeElement> emptyTypeElements = Collections.emptySet(); 621 while(innerIter.hasNext()) { 622 ProcessorState ps = innerIter.next(); 623 if (ps.contributed) 624 callProcessor(ps.processor, emptyTypeElements, re); 625 } 626 } 627 } 628 } 629 630 Iterator<? extends Processor> processorIterator; 631 ArrayList<ProcessorState> procStateList; 632 633 public ProcessorStateIterator iterator() { 634 return new ProcessorStateIterator(this); 635 } 636 637 DiscoveredProcessors(Iterator<? extends Processor> processorIterator) { 638 this.processorIterator = processorIterator; 639 this.procStateList = new ArrayList<ProcessorState>(); 640 } 641 642 /** 643 * Free jar files, etc. if using a service loader. 644 */ 645 public void close() { 646 if (processorIterator != null && 647 processorIterator instanceof ServiceIterator) { 648 ((ServiceIterator) processorIterator).close(); 649 } 650 } 651 } 652 653 private void discoverAndRunProcs(Context context, 654 Set<TypeElement> annotationsPresent, 655 List<ClassSymbol> topLevelClasses, 656 List<PackageSymbol> packageInfoFiles) { 657 // Writer for -XprintRounds and -XprintProcessorInfo data 658 PrintWriter xout = context.get(Log.outKey); 659 660 Map<String, TypeElement> unmatchedAnnotations = 661 new HashMap<String, TypeElement>(annotationsPresent.size()); 662 663 for(TypeElement a : annotationsPresent) { 664 unmatchedAnnotations.put(a.getQualifiedName().toString(), 665 a); 666 } 667 668 // Give "*" processors a chance to match 669 if (unmatchedAnnotations.size() == 0) 670 unmatchedAnnotations.put("", null); 671 672 DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator(); 673 // TODO: Create proper argument values; need past round 674 // information to fill in this constructor. Note that the 1 675 // st round of processing could be the last round if there 676 // were parse errors on the initial source files; however, we 677 // are not doing processing in that case. 678 679 Set<Element> rootElements = new LinkedHashSet<Element>(); 680 rootElements.addAll(topLevelClasses); 681 rootElements.addAll(packageInfoFiles); 682 rootElements = Collections.unmodifiableSet(rootElements); 683 684 RoundEnvironment renv = new JavacRoundEnvironment(false, 685 false, 686 rootElements, 687 JavacProcessingEnvironment.this); 688 689 while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) { 690 ProcessorState ps = psi.next(); 691 Set<String> matchedNames = new HashSet<String>(); 692 Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>(); 693 694 for (Map.Entry<String, TypeElement> entry: unmatchedAnnotations.entrySet()) { 695 String unmatchedAnnotationName = entry.getKey(); 696 if (ps.annotationSupported(unmatchedAnnotationName) ) { 697 matchedNames.add(unmatchedAnnotationName); 698 TypeElement te = entry.getValue(); 699 if (te != null) 700 typeElements.add(te); 701 } 702 } 703 704 if (matchedNames.size() > 0 || ps.contributed) { 705 foundTypeProcessors = foundTypeProcessors || (ps.processor instanceof AbstractTypeProcessor); 706 boolean processingResult = callProcessor(ps.processor, typeElements, renv); 707 ps.contributed = true; 708 ps.removeSupportedOptions(unmatchedProcessorOptions); 709 710 if (printProcessorInfo || verbose) { 711 xout.println(Log.getLocalizedString("x.print.processor.info", 712 ps.processor.getClass().getName(), 713 matchedNames.toString(), 714 processingResult)); 715 } 716 717 if (processingResult) { 718 unmatchedAnnotations.keySet().removeAll(matchedNames); 719 } 720 721 } 722 } 723 unmatchedAnnotations.remove(""); 724 725 if (lint && unmatchedAnnotations.size() > 0) { 726 // Remove annotations processed by javac 727 unmatchedAnnotations.keySet().removeAll(platformAnnotations); 728 if (unmatchedAnnotations.size() > 0) { 729 log = Log.instance(context); 730 log.warning("proc.annotations.without.processors", 731 unmatchedAnnotations.keySet()); 732 } 733 } 734 735 // Run contributing processors that haven't run yet 736 psi.runContributingProcs(renv); 737 738 // Debugging 739 if (options.get("displayFilerState") != null) 740 filer.displayState(); 741 } 742 743 /** 744 * Computes the set of annotations on the symbol in question. 745 * Leave class public for external testing purposes. 746 */ 747 public static class ComputeAnnotationSet extends 748 ElementScanner7<Set<TypeElement>, Set<TypeElement>> { 749 final Elements elements; 750 751 public ComputeAnnotationSet(Elements elements) { 752 super(); 753 this.elements = elements; 754 } 755 756 @Override 757 public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) { 758 // Don't scan enclosed elements of a package 759 return p; 760 } 761 762 @Override 763 public Set<TypeElement> scan(Element e, Set<TypeElement> p) { 764 for (AnnotationMirror annotationMirror : 765 elements.getAllAnnotationMirrors(e) ) { 766 Element e2 = annotationMirror.getAnnotationType().asElement(); 767 p.add((TypeElement) e2); 768 } 769 return super.scan(e, p); 770 } 771 } 772 773 private boolean callProcessor(Processor proc, 774 Set<? extends TypeElement> tes, 775 RoundEnvironment renv) { 776 try { 777 return proc.process(tes, renv); 778 } catch (CompletionFailure ex) { 779 StringWriter out = new StringWriter(); 780 ex.printStackTrace(new PrintWriter(out)); 781 log.error("proc.cant.access", ex.sym, ex.getDetailValue(), out.toString()); 782 return false; 783 } catch (Throwable t) { 784 throw new AnnotationProcessingError(t); 785 } 786 } 787 788 789 // TODO: internal catch clauses?; catch and rethrow an annotation 790 // processing error 791 public JavaCompiler doProcessing(Context context, 792 List<JCCompilationUnit> roots, 793 List<ClassSymbol> classSymbols, 794 Iterable<? extends PackageSymbol> pckSymbols) 795 throws IOException { 796 797 log = Log.instance(context); 798 // Writer for -XprintRounds and -XprintProcessorInfo data 799 PrintWriter xout = context.get(Log.outKey); 800 TaskListener taskListener = context.get(TaskListener.class); 801 802 JavaCompiler compiler = JavaCompiler.instance(context); 803 compiler.todo.clear(); // free the compiler's resources 804 805 int round = 0; 806 807 // List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots); 808 List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots); 809 810 for (ClassSymbol classSym : classSymbols) 811 topLevelClasses = topLevelClasses.prepend(classSym); 812 List<PackageSymbol> packageInfoFiles = 813 getPackageInfoFiles(roots); 814 815 Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>(); 816 for (PackageSymbol psym : pckSymbols) 817 specifiedPackages.add(psym); 818 this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages); 819 820 // Use annotation processing to compute the set of annotations present 821 Set<TypeElement> annotationsPresent = new LinkedHashSet<TypeElement>(); 822 ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils); 823 for (ClassSymbol classSym : topLevelClasses) 824 annotationComputer.scan(classSym, annotationsPresent); 825 for (PackageSymbol pkgSym : packageInfoFiles) 826 annotationComputer.scan(pkgSym, annotationsPresent); 827 828 Context currentContext = context; 829 830 int roundNumber = 0; 831 boolean errorStatus = false; 832 833 runAround: 834 while(true) { 835 if (fatalErrors && compiler.errorCount() != 0) { 836 errorStatus = true; 837 break runAround; 838 } 839 840 this.context = currentContext; 841 roundNumber++; 842 printRoundInfo(xout, roundNumber, topLevelClasses, annotationsPresent, false); 843 844 if (taskListener != null) 845 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); 846 847 try { 848 discoverAndRunProcs(currentContext, annotationsPresent, topLevelClasses, packageInfoFiles); 849 } finally { 850 if (taskListener != null) 851 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); 852 } 853 854 /* 855 * Processors for round n have run to completion. Prepare 856 * for round (n+1) by checked for errors raised by 857 * annotation processors and then checking for syntax 858 * errors on any generated source files. 859 */ 860 if (messager.errorRaised()) { 861 errorStatus = true; 862 break runAround; 863 } else { 864 if (moreToDo()) { 865 // annotationsPresentInSource = List.nil(); 866 annotationsPresent = new LinkedHashSet<TypeElement>(); 867 topLevelClasses = List.nil(); 868 packageInfoFiles = List.nil(); 869 870 compiler.close(false); 871 currentContext = contextForNextRound(currentContext, true); 872 873 JavaFileManager fileManager = currentContext.get(JavaFileManager.class); 874 875 compiler = JavaCompiler.instance(currentContext); 876 List<JCCompilationUnit> parsedFiles = sourcesToParsedFiles(compiler); 877 roots = cleanTrees(roots).appendList(parsedFiles); 878 879 // Check for errors after parsing 880 if (log.unrecoverableError) { 881 errorStatus = true; 882 break runAround; 883 } else { 884 List<ClassSymbol> newClasses = enterNewClassFiles(currentContext); 885 compiler.enterTrees(roots); 886 887 // annotationsPresentInSource = 888 // collector.findAnnotations(parsedFiles); 889 ListBuffer<ClassSymbol> tlc = new ListBuffer<ClassSymbol>(); 890 tlc.appendList(getTopLevelClasses(parsedFiles)); 891 tlc.appendList(getTopLevelClassesFromClasses(newClasses)); 892 topLevelClasses = tlc.toList(); 893 894 ListBuffer<PackageSymbol> pif = new ListBuffer<PackageSymbol>(); 895 pif.appendList(getPackageInfoFiles(parsedFiles)); 896 pif.appendList(getPackageInfoFilesFromClasses(newClasses)); 897 packageInfoFiles = pif.toList(); 898 899 annotationsPresent = new LinkedHashSet<TypeElement>(); 900 for (ClassSymbol classSym : topLevelClasses) 901 annotationComputer.scan(classSym, annotationsPresent); 902 for (PackageSymbol pkgSym : packageInfoFiles) 903 annotationComputer.scan(pkgSym, annotationsPresent); 904 905 updateProcessingState(currentContext, false); 906 } 907 } else 908 break runAround; // No new files 909 } 910 } 911 roots = runLastRound(xout, roundNumber, errorStatus, compiler, roots, taskListener); 912 // Set error status for any files compiled and generated in 913 // the last round 914 if (log.unrecoverableError) 915 errorStatus = true; 916 917 compiler.close(false); 918 currentContext = contextForNextRound(currentContext, true); 919 compiler = JavaCompiler.instance(currentContext); 920 921 filer.newRound(currentContext, true); 922 filer.warnIfUnclosedFiles(); 923 warnIfUnmatchedOptions(); 924 925 /* 926 * If an annotation processor raises an error in a round, 927 * that round runs to completion and one last round occurs. 928 * The last round may also occur because no more source or 929 * class files have been generated. Therefore, if an error 930 * was raised on either of the last *two* rounds, the compile 931 * should exit with a nonzero exit code. The current value of 932 * errorStatus holds whether or not an error was raised on the 933 * second to last round; errorRaised() gives the error status 934 * of the last round. 935 */ 936 errorStatus = errorStatus || messager.errorRaised(); 937 938 939 // Free resources 940 this.close(); 941 942 if (taskListener != null) 943 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING)); 944 945 if (errorStatus) { 946 compiler.log.nerrors += messager.errorCount(); 947 if (compiler.errorCount() == 0) 948 compiler.log.nerrors++; 949 } else if (procOnly && !foundTypeProcessors) { 950 compiler.todo.clear(); 951 } else { // Final compilation 952 compiler.close(false); 953 currentContext = contextForNextRound(currentContext, true); 954 this.context = currentContext; 955 updateProcessingState(currentContext, true); 956 compiler = JavaCompiler.instance(currentContext); 957 if (procOnly && foundTypeProcessors) 958 compiler.shouldStopPolicy = CompileState.FLOW; 959 960 if (true) { 961 compiler.enterTrees(cleanTrees(roots)); 962 } else { 963 List<JavaFileObject> fileObjects = List.nil(); 964 for (JCCompilationUnit unit : roots) 965 fileObjects = fileObjects.prepend(unit.getSourceFile()); 966 roots = null; 967 compiler.enterTrees(compiler.parseFiles(fileObjects.reverse())); 968 } 969 } 970 971 return compiler; 972 } 973 974 private List<JCCompilationUnit> sourcesToParsedFiles(JavaCompiler compiler) 975 throws IOException { 976 List<JavaFileObject> fileObjects = List.nil(); 977 for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) { 978 fileObjects = fileObjects.prepend(jfo); 979 } 980 981 return compiler.parseFiles(fileObjects); 982 } 983 984 // Call the last round of annotation processing 985 private List<JCCompilationUnit> runLastRound(PrintWriter xout, 986 int roundNumber, 987 boolean errorStatus, 988 JavaCompiler compiler, 989 List<JCCompilationUnit> roots, 990 TaskListener taskListener) throws IOException { 991 roundNumber++; 992 List<ClassSymbol> noTopLevelClasses = List.nil(); 993 Set<TypeElement> noAnnotations = Collections.emptySet(); 994 printRoundInfo(xout, roundNumber, noTopLevelClasses, noAnnotations, true); 995 996 Set<Element> emptyRootElements = Collections.emptySet(); // immutable 997 RoundEnvironment renv = new JavacRoundEnvironment(true, 998 errorStatus, 999 emptyRootElements, 1000 JavacProcessingEnvironment.this); 1001 if (taskListener != null) 1002 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); 1003 1004 try { 1005 discoveredProcs.iterator().runContributingProcs(renv); 1006 } finally { 1007 if (taskListener != null) 1008 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND)); 1009 } 1010 1011 // Add any sources generated during the last round to the set 1012 // of files to be compiled. 1013 if (moreToDo()) { 1014 List<JCCompilationUnit> parsedFiles = sourcesToParsedFiles(compiler); 1015 roots = cleanTrees(roots).appendList(parsedFiles); 1016 } 1017 1018 return roots; 1019 } 1020 1021 private void updateProcessingState(Context currentContext, boolean lastRound) { 1022 filer.newRound(currentContext, lastRound); 1023 messager.newRound(currentContext); 1024 1025 elementUtils.setContext(currentContext); 1026 typeUtils.setContext(currentContext); 1027 } 1028 1029 private void warnIfUnmatchedOptions() { 1030 if (!unmatchedProcessorOptions.isEmpty()) { 1031 log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString()); 1032 } 1033 } 1034 1035 private void printRoundInfo(PrintWriter xout, 1036 int roundNumber, 1037 List<ClassSymbol> topLevelClasses, 1038 Set<TypeElement> annotationsPresent, 1039 boolean lastRound) { 1040 if (printRounds || verbose) { 1041 xout.println(Log.getLocalizedString("x.print.rounds", 1042 roundNumber, 1043 "{" + topLevelClasses.toString(", ") + "}", 1044 annotationsPresent, 1045 lastRound)); 1046 } 1047 } 1048 1049 private List<ClassSymbol> enterNewClassFiles(Context currentContext) { 1050 ClassReader reader = ClassReader.instance(currentContext); 1051 Names names = Names.instance(currentContext); 1052 List<ClassSymbol> list = List.nil(); 1053 1054 for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) { 1055 Name name = names.fromString(entry.getKey()); 1056 JavaFileObject file = entry.getValue(); 1057 if (file.getKind() != JavaFileObject.Kind.CLASS) 1058 throw new AssertionError(file); 1059 ClassSymbol cs; 1060 if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) { 1061 Name packageName = Convert.packagePart(name); 1062 PackageSymbol p = reader.enterPackage(packageName); 1063 if (p.package_info == null) 1064 p.package_info = reader.enterClass(Convert.shortName(name), p); 1065 cs = p.package_info; 1066 if (cs.classfile == null) 1067 cs.classfile = file; 1068 } else 1069 cs = reader.enterClass(name, file); 1070 list = list.prepend(cs); 1071 } 1072 return list.reverse(); 1073 } 1074 1075 /** 1076 * Free resources related to annotation processing. 1077 */ 1078 public void close() throws IOException { 1079 filer.close(); 1080 if (discoveredProcs != null) // Make calling close idempotent 1081 discoveredProcs.close(); 1082 discoveredProcs = null; 1083 if (processorClassLoader != null && processorClassLoader instanceof Closeable) 1084 ((Closeable) processorClassLoader).close(); 1085 } 1086 1087 private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) { 1088 List<ClassSymbol> classes = List.nil(); 1089 for (JCCompilationUnit unit : units) { 1090 for (JCTree node : unit.defs) { 1091 if (node.getTag() == JCTree.CLASSDEF) { 1092 classes = classes.prepend(((JCClassDecl) node).sym); 1093 } 1094 } 1095 } 1096 return classes.reverse(); 1097 } 1098 1099 private List<ClassSymbol> getTopLevelClassesFromClasses(List<? extends ClassSymbol> syms) { 1100 List<ClassSymbol> classes = List.nil(); 1101 for (ClassSymbol sym : syms) { 1102 if (!isPkgInfo(sym)) { 1103 classes = classes.prepend(sym); 1104 } 1105 } 1106 return classes.reverse(); 1107 } 1108 1109 private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) { 1110 List<PackageSymbol> packages = List.nil(); 1111 for (JCCompilationUnit unit : units) { 1112 if (isPkgInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE)) { 1113 packages = packages.prepend(unit.packge); 1114 } 1115 } 1116 return packages.reverse(); 1117 } 1118 1119 private List<PackageSymbol> getPackageInfoFilesFromClasses(List<? extends ClassSymbol> syms) { 1120 List<PackageSymbol> packages = List.nil(); 1121 for (ClassSymbol sym : syms) { 1122 if (isPkgInfo(sym)) { 1123 packages = packages.prepend((PackageSymbol) sym.owner); 1124 } 1125 } 1126 return packages.reverse(); 1127 } 1128 1129 private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) { 1130 return fo.isNameCompatible("package-info", kind); 1131 } 1132 1133 private boolean isPkgInfo(ClassSymbol sym) { 1134 return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym); 1135 } 1136 1137 private Context contextForNextRound(Context context, boolean shareNames) 1138 throws IOException 1139 { 1140 Context next = new Context(); 1141 1142 Options options = Options.instance(context); 1143 assert options != null; 1144 next.put(Options.optionsKey, options); 1145 1146 PrintWriter out = context.get(Log.outKey); 1147 assert out != null; 1148 next.put(Log.outKey, out); 1149 1150 if (shareNames) { 1151 Names names = Names.instance(context); 1152 assert names != null; 1153 next.put(Names.namesKey, names); 1154 } 1155 1156 DiagnosticListener<?> dl = context.get(DiagnosticListener.class); 1157 if (dl != null) 1158 next.put(DiagnosticListener.class, dl); 1159 1160 TaskListener tl = context.get(TaskListener.class); 1161 if (tl != null) 1162 next.put(TaskListener.class, tl); 1163 1164 JavaFileManager jfm = context.get(JavaFileManager.class); 1165 assert jfm != null; 1166 next.put(JavaFileManager.class, jfm); 1167 if (jfm instanceof JavacFileManager) { 1168 ((JavacFileManager)jfm).setContext(next); 1169 } 1170 1171 Names names = Names.instance(context); 1172 assert names != null; 1173 next.put(Names.namesKey, names); 1174 1175 Keywords keywords = Keywords.instance(context); 1176 assert(keywords != null); 1177 next.put(Keywords.keywordsKey, keywords); 1178 1179 JavaCompiler oldCompiler = JavaCompiler.instance(context); 1180 JavaCompiler nextCompiler = JavaCompiler.instance(next); 1181 nextCompiler.initRound(oldCompiler); 1182 1183 JavacTaskImpl task = context.get(JavacTaskImpl.class); 1184 if (task != null) { 1185 next.put(JavacTaskImpl.class, task); 1186 task.updateContext(next); 1187 } 1188 1189 context.clear(); 1190 return next; 1191 } 1192 1193 /* 1194 * Called retroactively to determine if a class loader was required, 1195 * after we have failed to create one. 1196 */ 1197 private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) { 1198 if (procNames != null) 1199 return true; 1200 1201 String procPath; 1202 URL[] urls = new URL[1]; 1203 for(File pathElement : workingpath) { 1204 try { 1205 urls[0] = pathElement.toURI().toURL(); 1206 if (ServiceProxy.hasService(Processor.class, urls)) 1207 return true; 1208 } catch (MalformedURLException ex) { 1209 throw new AssertionError(ex); 1210 } 1211 catch (ServiceProxy.ServiceConfigurationError e) { 1212 log.error("proc.bad.config.file", e.getLocalizedMessage()); 1213 return true; 1214 } 1215 } 1216 1217 return false; 1218 } 1219 1220 private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) { 1221 for (T node : nodes) 1222 treeCleaner.scan(node); 1223 return nodes; 1224 } 1225 1226 private static TreeScanner treeCleaner = new TreeScanner() { 1227 public void scan(JCTree node) { 1228 super.scan(node); 1229 if (node != null) 1230 node.type = null; 1231 } 1232 public void visitTopLevel(JCCompilationUnit node) { 1233 node.packge = null; 1234 super.visitTopLevel(node); 1235 } 1236 public void visitClassDef(JCClassDecl node) { 1237 node.sym = null; 1238 super.visitClassDef(node); 1239 } 1240 public void visitMethodDef(JCMethodDecl node) { 1241 node.sym = null; 1242 super.visitMethodDef(node); 1243 } 1244 public void visitVarDef(JCVariableDecl node) { 1245 node.sym = null; 1246 super.visitVarDef(node); 1247 } 1248 public void visitNewClass(JCNewClass node) { 1249 node.constructor = null; 1250 super.visitNewClass(node); 1251 } 1252 public void visitAssignop(JCAssignOp node) { 1253 node.operator = null; 1254 super.visitAssignop(node); 1255 } 1256 public void visitUnary(JCUnary node) { 1257 node.operator = null; 1258 super.visitUnary(node); 1259 } 1260 public void visitBinary(JCBinary node) { 1261 node.operator = null; 1262 super.visitBinary(node); 1263 } 1264 public void visitSelect(JCFieldAccess node) { 1265 node.sym = null; 1266 super.visitSelect(node); 1267 } 1268 public void visitIdent(JCIdent node) { 1269 node.sym = null; 1270 super.visitIdent(node); 1271 } 1272 }; 1273 1274 1275 private boolean moreToDo() { 1276 return filer.newFiles(); 1277 } 1278 1279 /** 1280 * {@inheritdoc} 1281 * 1282 * Command line options suitable for presenting to annotation 1283 * processors. "-Afoo=bar" should be "-Afoo" => "bar". 1284 */ 1285 public Map<String,String> getOptions() { 1286 return processorOptions; 1287 } 1288 1289 public Messager getMessager() { 1290 return messager; 1291 } 1292 1293 public Filer getFiler() { 1294 return filer; 1295 } 1296 1297 public JavacElements getElementUtils() { 1298 return elementUtils; 1299 } 1300 1301 public JavacTypes getTypeUtils() { 1302 return typeUtils; 1303 } 1304 1305 public SourceVersion getSourceVersion() { 1306 return Source.toSourceVersion(source); 1307 } 1308 1309 public Locale getLocale() { 1310 return messages.getCurrentLocale(); 1311 } 1312 1313 public Set<Symbol.PackageSymbol> getSpecifiedPackages() { 1314 return specifiedPackages; 1315 } 1316 1317 private static final Pattern allMatches = Pattern.compile(".*"); 1318 public static final Pattern noMatches = Pattern.compile("(\\P{all})+"); 1319 1320 /** 1321 * Convert import-style string for supported annotations into a 1322 * regex matching that string. If the string is a valid 1323 * import-style string, return a regex that won't match anything. 1324 */ 1325 private static Pattern importStringToPattern(String s, Processor p, Log log) { 1326 if (isValidImportString(s)) { 1327 return validImportStringToPattern(s); 1328 } else { 1329 log.warning("proc.malformed.supported.string", s, p.getClass().getName()); 1330 return noMatches; // won't match any valid identifier 1331 } 1332 } 1333 1334 /** 1335 * Return true if the argument string is a valid import-style 1336 * string specifying claimed annotations; return false otherwise. 1337 */ 1338 public static boolean isValidImportString(String s) { 1339 if (s.equals("*")) 1340 return true; 1341 1342 boolean valid = true; 1343 String t = s; 1344 int index = t.indexOf('*'); 1345 1346 if (index != -1) { 1347 // '*' must be last character... 1348 if (index == t.length() -1) { 1349 // ... any and preceding character must be '.' 1350 if ( index-1 >= 0 ) { 1351 valid = t.charAt(index-1) == '.'; 1352 // Strip off ".*$" for identifier checks 1353 t = t.substring(0, t.length()-2); 1354 } 1355 } else 1356 return false; 1357 } 1358 1359 // Verify string is off the form (javaId \.)+ or javaId 1360 if (valid) { 1361 String[] javaIds = t.split("\\.", t.length()+2); 1362 for(String javaId: javaIds) 1363 valid &= SourceVersion.isIdentifier(javaId); 1364 } 1365 return valid; 1366 } 1367 1368 public static Pattern validImportStringToPattern(String s) { 1369 if (s.equals("*")) { 1370 return allMatches; 1371 } else { 1372 String s_prime = s.replace(".", "\\."); 1373 1374 if (s_prime.endsWith("*")) { 1375 s_prime = s_prime.substring(0, s_prime.length() - 1) + ".+"; 1376 } 1377 1378 return Pattern.compile(s_prime); 1379 } 1380 } 1381 1382 /** 1383 * For internal use by Sun Microsystems only. This method will be 1384 * removed without warning. 1385 */ 1386 public Context getContext() { 1387 return context; 1388 } 1389 1390 public String toString() { 1391 return "javac ProcessingEnvironment"; 1392 } 1393 1394 public static boolean isValidOptionName(String optionName) { 1395 for(String s : optionName.split("\\.", -1)) { 1396 if (!SourceVersion.isIdentifier(s)) 1397 return false; 1398 } 1399 return true; 1400 } 1401 }