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