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