src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java

Tue, 16 Sep 2008 18:35:18 -0700

author
jjg
date
Tue, 16 Sep 2008 18:35:18 -0700
changeset 113
eff38cc97183
parent 54
eaf608c64fec
child 136
8eafba4f61be
permissions
-rw-r--r--

6574134: Allow for alternative implementation of Name Table with garbage collection of name bytes
Reviewed-by: darcy, mcimadamore

     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.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     private Context context;
   138    public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
   139         options = Options.instance(context);
   140         this.context = context;
   141         log = Log.instance(context);
   142         source = Source.instance(context);
   143         printProcessorInfo = options.get("-XprintProcessorInfo") != null;
   144         printRounds = options.get("-XprintRounds") != null;
   145         verbose = options.get("-verbose") != null;
   146         lint = options.lint("processing");
   147         procOnly = options.get("-proc:only") != null ||
   148             options.get("-Xprint") != null;
   149         fatalErrors = options.get("fatalEnterError") != null;
   150         platformAnnotations = initPlatformAnnotations();
   152         // Initialize services before any processors are initialzied
   153         // in case processors use them.
   154         filer = new JavacFiler(context);
   155         messager = new JavacMessager(context, this);
   156         elementUtils = new JavacElements(context);
   157         typeUtils = new JavacTypes(context);
   158         processorOptions = initProcessorOptions(context);
   159         unmatchedProcessorOptions = initUnmatchedProcessorOptions();
   160         initProcessorIterator(context, processors);
   161     }
   163     private Set<String> initPlatformAnnotations() {
   164         Set<String> platformAnnotations = new HashSet<String>();
   165         platformAnnotations.add("java.lang.Deprecated");
   166         platformAnnotations.add("java.lang.Override");
   167         platformAnnotations.add("java.lang.SuppressWarnings");
   168         platformAnnotations.add("java.lang.annotation.Documented");
   169         platformAnnotations.add("java.lang.annotation.Inherited");
   170         platformAnnotations.add("java.lang.annotation.Retention");
   171         platformAnnotations.add("java.lang.annotation.Target");
   172         return Collections.unmodifiableSet(platformAnnotations);
   173     }
   175     private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
   176         Paths paths = Paths.instance(context);
   177         Log   log   = Log.instance(context);
   178         Iterator<? extends Processor> processorIterator;
   180         if (options.get("-Xprint") != null) {
   181             try {
   182                 Processor processor = PrintingProcessor.class.newInstance();
   183                 processorIterator = List.of(processor).iterator();
   184             } catch (Throwable t) {
   185                 AssertionError assertError =
   186                     new AssertionError("Problem instantiating PrintingProcessor.");
   187                 assertError.initCause(t);
   188                 throw assertError;
   189             }
   190         } else if (processors != null) {
   191             processorIterator = processors.iterator();
   192         } else {
   193             String processorNames = options.get("-processor");
   194             JavaFileManager fileManager = context.get(JavaFileManager.class);
   195             try {
   196                 // If processorpath is not explicitly set, use the classpath.
   197                 ClassLoader processorCL = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   198                     ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
   199                     : fileManager.getClassLoader(CLASS_PATH);
   201                 /*
   202                  * If the "-processor" option is used, search the appropriate
   203                  * path for the named class.  Otherwise, use a service
   204                  * provider mechanism to create the processor iterator.
   205                  */
   206                 if (processorNames != null) {
   207                     processorIterator = new NameProcessIterator(processorNames, processorCL, log);
   208                 } else {
   209                     processorIterator = new ServiceIterator(processorCL, log);
   210                 }
   211             } catch (SecurityException e) {
   212                 /*
   213                  * A security exception will occur if we can't create a classloader.
   214                  * Ignore the exception if, with hindsight, we didn't need it anyway
   215                  * (i.e. no processor was specified either explicitly, or implicitly,
   216                  * in service configuration file.) Otherwise, we cannot continue.
   217                  */
   218                 processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
   219             }
   220         }
   221         discoveredProcs = new DiscoveredProcessors(processorIterator);
   222     }
   224     /**
   225      * Returns an empty processor iterator if no processors are on the
   226      * relevant path, otherwise if processors are present, logs an
   227      * error.  Called when a service loader is unavailable for some
   228      * reason, either because a service loader class cannot be found
   229      * or because a security policy prevents class loaders from being
   230      * created.
   231      *
   232      * @param key The resource key to use to log an error message
   233      * @param e   If non-null, pass this exception to Abort
   234      */
   235     private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) {
   236         JavaFileManager fileManager = context.get(JavaFileManager.class);
   238         if (fileManager instanceof JavacFileManager) {
   239             StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager;
   240             Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   241                 ? standardFileManager.getLocation(ANNOTATION_PROCESSOR_PATH)
   242                 : standardFileManager.getLocation(CLASS_PATH);
   244             if (needClassLoader(options.get("-processor"), workingPath) )
   245                 handleException(key, e);
   247         } else {
   248             handleException(key, e);
   249         }
   251         java.util.List<Processor> pl = Collections.emptyList();
   252         return pl.iterator();
   253     }
   255     /**
   256      * Handle a security exception thrown during initializing the
   257      * Processor iterator.
   258      */
   259     private void handleException(String key, Exception e) {
   260         if (e != null) {
   261             log.error(key, e.getLocalizedMessage());
   262             throw new Abort(e);
   263         } else {
   264             log.error(key);
   265             throw new Abort();
   266         }
   267     }
   269     /**
   270      * Use a service loader appropriate for the platform to provide an
   271      * iterator over annotations processors.  If
   272      * java.util.ServiceLoader is present use it, otherwise, use
   273      * sun.misc.Service, otherwise fail if a loader is needed.
   274      */
   275     private class ServiceIterator implements Iterator<Processor> {
   276         // The to-be-wrapped iterator.
   277         private Iterator<?> iterator;
   278         private Log log;
   280         ServiceIterator(ClassLoader classLoader, Log log) {
   281             Class<?> loaderClass;
   282             String loadMethodName;
   283             boolean jusl;
   285             this.log = log;
   286             try {
   287                 try {
   288                     loaderClass = Class.forName("java.util.ServiceLoader");
   289                     loadMethodName = "load";
   290                     jusl = true;
   291                 } catch (ClassNotFoundException cnfe) {
   292                     try {
   293                         loaderClass = Class.forName("sun.misc.Service");
   294                         loadMethodName = "providers";
   295                         jusl = false;
   296                     } catch (ClassNotFoundException cnfe2) {
   297                         // Fail softly if a loader is not actually needed.
   298                         this.iterator = handleServiceLoaderUnavailability("proc.no.service",
   299                                                                           null);
   300                         return;
   301                     }
   302                 }
   304                 // java.util.ServiceLoader.load or sun.misc.Service.providers
   305                 Method loadMethod = loaderClass.getMethod(loadMethodName,
   306                                                           Class.class,
   307                                                           ClassLoader.class);
   309                 Object result = loadMethod.invoke(null,
   310                                                   Processor.class,
   311                                                   classLoader);
   313                 // For java.util.ServiceLoader, we have to call another
   314                 // method to get the iterator.
   315                 if (jusl) {
   316                     Method m = loaderClass.getMethod("iterator");
   317                     result = m.invoke(result); // serviceLoader.iterator();
   318                 }
   320                 // The result should now be an iterator.
   321                 this.iterator = (Iterator<?>) result;
   322             } catch (Throwable t) {
   323                 log.error("proc.service.problem");
   324                 throw new Abort(t);
   325             }
   326         }
   328         public boolean hasNext() {
   329             try {
   330                 return iterator.hasNext();
   331             } catch (Throwable t) {
   332                 if ("ServiceConfigurationError".
   333                     equals(t.getClass().getSimpleName())) {
   334                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   335                 }
   336                 throw new Abort(t);
   337             }
   338         }
   340         public Processor next() {
   341             try {
   342                 return (Processor)(iterator.next());
   343             } catch (Throwable t) {
   344                 if ("ServiceConfigurationError".
   345                     equals(t.getClass().getSimpleName())) {
   346                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   347                 } else {
   348                     log.error("proc.processor.constructor.error", t.getLocalizedMessage());
   349                 }
   350                 throw new Abort(t);
   351             }
   352         }
   354         public void remove() {
   355             throw new UnsupportedOperationException();
   356         }
   357     }
   360     private static class NameProcessIterator implements Iterator<Processor> {
   361         Processor nextProc = null;
   362         Iterator<String> names;
   363         ClassLoader processorCL;
   364         Log log;
   366         NameProcessIterator(String names, ClassLoader processorCL, Log log) {
   367             this.names = Arrays.asList(names.split(",")).iterator();
   368             this.processorCL = processorCL;
   369             this.log = log;
   370         }
   372         public boolean hasNext() {
   373             if (nextProc != null)
   374                 return true;
   375             else {
   376                 if (!names.hasNext())
   377                     return false;
   378                 else {
   379                     String processorName = names.next();
   381                     Processor processor;
   382                     try {
   383                         try {
   384                             processor =
   385                                 (Processor) (processorCL.loadClass(processorName).newInstance());
   386                         } catch (ClassNotFoundException cnfe) {
   387                             log.error("proc.processor.not.found", processorName);
   388                             return false;
   389                         } catch (ClassCastException cce) {
   390                             log.error("proc.processor.wrong.type", processorName);
   391                             return false;
   392                         } catch (Exception e ) {
   393                             log.error("proc.processor.cant.instantiate", processorName);
   394                             return false;
   395                         }
   396                     } catch(Throwable t) {
   397                         throw new AnnotationProcessingError(t);
   398                     }
   399                     nextProc = processor;
   400                     return true;
   401                 }
   403             }
   404         }
   406         public Processor next() {
   407             if (hasNext()) {
   408                 Processor p = nextProc;
   409                 nextProc = null;
   410                 return p;
   411             } else
   412                 throw new NoSuchElementException();
   413         }
   415         public void remove () {
   416             throw new UnsupportedOperationException();
   417         }
   418     }
   420     public boolean atLeastOneProcessor() {
   421         return discoveredProcs.iterator().hasNext();
   422     }
   424     private Map<String, String> initProcessorOptions(Context context) {
   425         Options options = Options.instance(context);
   426         Set<String> keySet = options.keySet();
   427         Map<String, String> tempOptions = new LinkedHashMap<String, String>();
   429         for(String key : keySet) {
   430             if (key.startsWith("-A") && key.length() > 2) {
   431                 int sepIndex = key.indexOf('=');
   432                 String candidateKey = null;
   433                 String candidateValue = null;
   435                 if (sepIndex == -1)
   436                     candidateKey = key.substring(2);
   437                 else if (sepIndex >= 3) {
   438                     candidateKey = key.substring(2, sepIndex);
   439                     candidateValue = (sepIndex < key.length()-1)?
   440                         key.substring(sepIndex+1) : null;
   441                 }
   442                 tempOptions.put(candidateKey, candidateValue);
   443             }
   444         }
   446         return Collections.unmodifiableMap(tempOptions);
   447     }
   449     private Set<String> initUnmatchedProcessorOptions() {
   450         Set<String> unmatchedProcessorOptions = new HashSet<String>();
   451         unmatchedProcessorOptions.addAll(processorOptions.keySet());
   452         return unmatchedProcessorOptions;
   453     }
   455     /**
   456      * State about how a processor has been used by the tool.  If a
   457      * processor has been used on a prior round, its process method is
   458      * called on all subsequent rounds, perhaps with an empty set of
   459      * annotations to process.  The {@code annotatedSupported} method
   460      * caches the supported annotation information from the first (and
   461      * only) getSupportedAnnotationTypes call to the processor.
   462      */
   463     static class ProcessorState {
   464         public Processor processor;
   465         public boolean   contributed;
   466         private ArrayList<Pattern> supportedAnnotationPatterns;
   467         private ArrayList<String>  supportedOptionNames;
   469         ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
   470             processor = p;
   471             contributed = false;
   473             try {
   474                 processor.init(env);
   476                 checkSourceVersionCompatibility(source, log);
   478                 supportedAnnotationPatterns = new ArrayList<Pattern>();
   479                 for (String importString : processor.getSupportedAnnotationTypes()) {
   480                     supportedAnnotationPatterns.add(importStringToPattern(importString,
   481                                                                           processor,
   482                                                                           log));
   483                 }
   485                 supportedOptionNames = new ArrayList<String>();
   486                 for (String optionName : processor.getSupportedOptions() ) {
   487                     if (checkOptionName(optionName, log))
   488                         supportedOptionNames.add(optionName);
   489                 }
   491             } catch (Throwable t) {
   492                 throw new AnnotationProcessingError(t);
   493             }
   494         }
   496         /**
   497          * Checks whether or not a processor's source version is
   498          * compatible with the compilation source version.  The
   499          * processor's source version needs to be greater than or
   500          * equal to the source version of the compile.
   501          */
   502         private void checkSourceVersionCompatibility(Source source, Log log) {
   503             SourceVersion procSourceVersion = processor.getSupportedSourceVersion();
   505             if (procSourceVersion.compareTo(Source.toSourceVersion(source)) < 0 )  {
   506                 log.warning("proc.processor.incompatible.source.version",
   507                             procSourceVersion,
   508                             processor.getClass().getName(),
   509                             source.name);
   510             }
   511         }
   513         private boolean checkOptionName(String optionName, Log log) {
   514             boolean valid = isValidOptionName(optionName);
   515             if (!valid)
   516                 log.error("proc.processor.bad.option.name",
   517                             optionName,
   518                             processor.getClass().getName());
   519             return valid;
   520         }
   522         public boolean annotationSupported(String annotationName) {
   523             for(Pattern p: supportedAnnotationPatterns) {
   524                 if (p.matcher(annotationName).matches())
   525                     return true;
   526             }
   527             return false;
   528         }
   530         /**
   531          * Remove options that are matched by this processor.
   532          */
   533         public void removeSupportedOptions(Set<String> unmatchedProcessorOptions) {
   534             unmatchedProcessorOptions.removeAll(supportedOptionNames);
   535         }
   536     }
   538     // TODO: These two classes can probably be rewritten better...
   539     /**
   540      * This class holds information about the processors that have
   541      * been discoverd so far as well as the means to discover more, if
   542      * necessary.  A single iterator should be used per round of
   543      * annotation processing.  The iterator first visits already
   544      * discovered processors then fails over to the service provided
   545      * mechanism if additional queries are made.
   546      */
   547     class DiscoveredProcessors implements Iterable<ProcessorState> {
   549         class ProcessorStateIterator implements Iterator<ProcessorState> {
   550             DiscoveredProcessors psi;
   551             Iterator<ProcessorState> innerIter;
   552             boolean onProcInterator;
   554             ProcessorStateIterator(DiscoveredProcessors psi) {
   555                 this.psi = psi;
   556                 this.innerIter = psi.procStateList.iterator();
   557                 this.onProcInterator = false;
   558             }
   560             public ProcessorState next() {
   561                 if (!onProcInterator) {
   562                     if (innerIter.hasNext())
   563                         return innerIter.next();
   564                     else
   565                         onProcInterator = true;
   566                 }
   568                 if (psi.processorIterator.hasNext()) {
   569                     ProcessorState ps = new ProcessorState(psi.processorIterator.next(),
   570                                                            log, source, JavacProcessingEnvironment.this);
   571                     psi.procStateList.add(ps);
   572                     return ps;
   573                 } else
   574                     throw new NoSuchElementException();
   575             }
   577             public boolean hasNext() {
   578                 if (onProcInterator)
   579                     return  psi.processorIterator.hasNext();
   580                 else
   581                     return innerIter.hasNext() || psi.processorIterator.hasNext();
   582             }
   584             public void remove () {
   585                 throw new UnsupportedOperationException();
   586             }
   588             /**
   589              * Run all remaining processors on the procStateList that
   590              * have not already run this round with an empty set of
   591              * annotations.
   592              */
   593             public void runContributingProcs(RoundEnvironment re) {
   594                 if (!onProcInterator) {
   595                     Set<TypeElement> emptyTypeElements = Collections.emptySet();
   596                     while(innerIter.hasNext()) {
   597                         ProcessorState ps = innerIter.next();
   598                         if (ps.contributed)
   599                             callProcessor(ps.processor, emptyTypeElements, re);
   600                     }
   601                 }
   602             }
   603         }
   605         Iterator<? extends Processor> processorIterator;
   606         ArrayList<ProcessorState>  procStateList;
   608         public ProcessorStateIterator iterator() {
   609             return new ProcessorStateIterator(this);
   610         }
   612         DiscoveredProcessors(Iterator<? extends Processor> processorIterator) {
   613             this.processorIterator = processorIterator;
   614             this.procStateList = new ArrayList<ProcessorState>();
   615         }
   616     }
   618     private void discoverAndRunProcs(Context context,
   619                                      Set<TypeElement> annotationsPresent,
   620                                      List<ClassSymbol> topLevelClasses,
   621                                      List<PackageSymbol> packageInfoFiles) {
   622         // Writer for -XprintRounds and -XprintProcessorInfo data
   623         PrintWriter xout = context.get(Log.outKey);
   625         Map<String, TypeElement> unmatchedAnnotations =
   626             new HashMap<String, TypeElement>(annotationsPresent.size());
   628         for(TypeElement a  : annotationsPresent) {
   629                 unmatchedAnnotations.put(a.getQualifiedName().toString(),
   630                                          a);
   631         }
   633         // Give "*" processors a chance to match
   634         if (unmatchedAnnotations.size() == 0)
   635             unmatchedAnnotations.put("", null);
   637         DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
   638         // TODO: Create proper argument values; need past round
   639         // information to fill in this constructor.  Note that the 1
   640         // st round of processing could be the last round if there
   641         // were parse errors on the initial source files; however, we
   642         // are not doing processing in that case.
   644         Set<Element> rootElements = new LinkedHashSet<Element>();
   645         rootElements.addAll(topLevelClasses);
   646         rootElements.addAll(packageInfoFiles);
   647         rootElements = Collections.unmodifiableSet(rootElements);
   649         RoundEnvironment renv = new JavacRoundEnvironment(false,
   650                                                           false,
   651                                                           rootElements,
   652                                                           JavacProcessingEnvironment.this);
   654         while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
   655             ProcessorState ps = psi.next();
   656             Set<String>  matchedNames = new HashSet<String>();
   657             Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
   658             for (String unmatchedAnnotationName : unmatchedAnnotations.keySet()) {
   659                 if (ps.annotationSupported(unmatchedAnnotationName) ) {
   660                     matchedNames.add(unmatchedAnnotationName);
   661                     TypeElement te = unmatchedAnnotations.get(unmatchedAnnotationName);
   662                     if (te != null)
   663                         typeElements.add(te);
   664                 }
   665             }
   667             if (matchedNames.size() > 0 || ps.contributed) {
   668                 boolean processingResult = callProcessor(ps.processor, typeElements, renv);
   669                 ps.contributed = true;
   670                 ps.removeSupportedOptions(unmatchedProcessorOptions);
   672                 if (printProcessorInfo || verbose) {
   673                     xout.println(Log.getLocalizedString("x.print.processor.info",
   674                                                         ps.processor.getClass().getName(),
   675                                                         matchedNames.toString(),
   676                                                         processingResult));
   677                 }
   679                 if (processingResult) {
   680                     unmatchedAnnotations.keySet().removeAll(matchedNames);
   681                 }
   683             }
   684         }
   685         unmatchedAnnotations.remove("");
   687         if (lint && unmatchedAnnotations.size() > 0) {
   688             // Remove annotations processed by javac
   689             unmatchedAnnotations.keySet().removeAll(platformAnnotations);
   690             if (unmatchedAnnotations.size() > 0) {
   691                 log = Log.instance(context);
   692                 log.warning("proc.annotations.without.processors",
   693                             unmatchedAnnotations.keySet());
   694             }
   695         }
   697         // Run contributing processors that haven't run yet
   698         psi.runContributingProcs(renv);
   700         // Debugging
   701         if (options.get("displayFilerState") != null)
   702             filer.displayState();
   703     }
   705     /**
   706      * Computes the set of annotations on the symbol in question.
   707      * Leave class public for external testing purposes.
   708      */
   709     public static class ComputeAnnotationSet extends
   710         ElementScanner6<Set<TypeElement>, Set<TypeElement>> {
   711         final Elements elements;
   713         public ComputeAnnotationSet(Elements elements) {
   714             super();
   715             this.elements = elements;
   716         }
   718         @Override
   719         public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
   720             // Don't scan enclosed elements of a package
   721             return p;
   722         }
   724         @Override
   725          public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
   726             for (AnnotationMirror annotationMirror :
   727                      elements.getAllAnnotationMirrors(e) ) {
   728                 Element e2 = annotationMirror.getAnnotationType().asElement();
   729                 p.add((TypeElement) e2);
   730             }
   731             return super.scan(e, p);
   732         }
   733     }
   735     private boolean callProcessor(Processor proc,
   736                                          Set<? extends TypeElement> tes,
   737                                          RoundEnvironment renv) {
   738         try {
   739             return proc.process(tes, renv);
   740         } catch (CompletionFailure ex) {
   741             StringWriter out = new StringWriter();
   742             ex.printStackTrace(new PrintWriter(out));
   743             log.error("proc.cant.access", ex.sym, ex.getDetailValue(), out.toString());
   744             return false;
   745         } catch (Throwable t) {
   746             throw new AnnotationProcessingError(t);
   747         }
   748     }
   751     // TODO: internal catch clauses?; catch and rethrow an annotation
   752     // processing error
   753     public JavaCompiler doProcessing(Context context,
   754                                      List<JCCompilationUnit> roots,
   755                                      List<ClassSymbol> classSymbols,
   756                                      Iterable<? extends PackageSymbol> pckSymbols)
   757     throws IOException {
   759         log = Log.instance(context);
   760         // Writer for -XprintRounds and -XprintProcessorInfo data
   761         PrintWriter xout = context.get(Log.outKey);
   762         TaskListener taskListener = context.get(TaskListener.class);
   765         AnnotationCollector collector = new AnnotationCollector();
   767         JavaCompiler compiler = JavaCompiler.instance(context);
   768         compiler.todo.clear(); // free the compiler's resources
   770         int round = 0;
   772         // List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots);
   773         List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots);
   775         for (ClassSymbol classSym : classSymbols)
   776             topLevelClasses = topLevelClasses.prepend(classSym);
   777         List<PackageSymbol> packageInfoFiles =
   778             getPackageInfoFiles(roots);
   780         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
   781         for (PackageSymbol psym : pckSymbols)
   782             specifiedPackages.add(psym);
   783         this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
   785         // Use annotation processing to compute the set of annotations present
   786         Set<TypeElement> annotationsPresent = new LinkedHashSet<TypeElement>();
   787         ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
   788         for (ClassSymbol classSym : topLevelClasses)
   789             annotationComputer.scan(classSym, annotationsPresent);
   790         for (PackageSymbol pkgSym : packageInfoFiles)
   791             annotationComputer.scan(pkgSym, annotationsPresent);
   793         Context currentContext = context;
   795         int roundNumber = 0;
   796         boolean errorStatus = false;
   798         runAround:
   799         while(true) {
   800             if (fatalErrors && compiler.errorCount() != 0) {
   801                 errorStatus = true;
   802                 break runAround;
   803             }
   805             this.context = currentContext;
   806             roundNumber++;
   807             printRoundInfo(xout, roundNumber, topLevelClasses, annotationsPresent, false);
   809             if (taskListener != null)
   810                 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   812             try {
   813                 discoverAndRunProcs(currentContext, annotationsPresent, topLevelClasses, packageInfoFiles);
   814             } finally {
   815                 if (taskListener != null)
   816                     taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   817             }
   819             /*
   820              * Processors for round n have run to completion.  Prepare
   821              * for round (n+1) by checked for errors raised by
   822              * annotation processors and then checking for syntax
   823              * errors on any generated source files.
   824              */
   825             if (messager.errorRaised()) {
   826                 errorStatus = true;
   827                 break runAround;
   828             } else {
   829                 if (moreToDo()) {
   830                     // annotationsPresentInSource = List.nil();
   831                     annotationsPresent = new LinkedHashSet<TypeElement>();
   832                     topLevelClasses  = List.nil();
   833                     packageInfoFiles = List.nil();
   835                     compiler.close(false);
   836                     currentContext = contextForNextRound(currentContext, true);
   838                     JavaFileManager fileManager = currentContext.get(JavaFileManager.class);
   840                     List<JavaFileObject> fileObjects = List.nil();
   841                     for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
   842                         fileObjects = fileObjects.prepend(jfo);
   843                     }
   846                     compiler = JavaCompiler.instance(currentContext);
   847                     List<JCCompilationUnit> parsedFiles = compiler.parseFiles(fileObjects);
   848                     roots = cleanTrees(roots).reverse();
   851                     for (JCCompilationUnit unit : parsedFiles)
   852                         roots = roots.prepend(unit);
   853                     roots = roots.reverse();
   855                     // Check for errors after parsing
   856                     if (compiler.parseErrors()) {
   857                         errorStatus = true;
   858                         break runAround;
   859                     } else {
   860                         ListBuffer<ClassSymbol> classes = enterNewClassFiles(currentContext);
   861                         compiler.enterTrees(roots);
   863                         // annotationsPresentInSource =
   864                         // collector.findAnnotations(parsedFiles);
   865                         classes.appendList(getTopLevelClasses(parsedFiles));
   866                         topLevelClasses  = classes.toList();
   867                         packageInfoFiles = getPackageInfoFiles(parsedFiles);
   869                         annotationsPresent = new LinkedHashSet<TypeElement>();
   870                         for (ClassSymbol classSym : topLevelClasses)
   871                             annotationComputer.scan(classSym, annotationsPresent);
   872                         for (PackageSymbol pkgSym : packageInfoFiles)
   873                             annotationComputer.scan(pkgSym, annotationsPresent);
   875                         updateProcessingState(currentContext, false);
   876                     }
   877                 } else
   878                     break runAround; // No new files
   879             }
   880         }
   881         runLastRound(xout, roundNumber, errorStatus, taskListener);
   883         compiler.close(false);
   884         currentContext = contextForNextRound(currentContext, true);
   885         compiler = JavaCompiler.instance(currentContext);
   886         filer.newRound(currentContext, true);
   887         filer.warnIfUnclosedFiles();
   888         warnIfUnmatchedOptions();
   890        /*
   891         * If an annotation processor raises an error in a round,
   892         * that round runs to completion and one last round occurs.
   893         * The last round may also occur because no more source or
   894         * class files have been generated.  Therefore, if an error
   895         * was raised on either of the last *two* rounds, the compile
   896         * should exit with a nonzero exit code.  The current value of
   897         * errorStatus holds whether or not an error was raised on the
   898         * second to last round; errorRaised() gives the error status
   899         * of the last round.
   900         */
   901        errorStatus = errorStatus || messager.errorRaised();
   904         // Free resources
   905         this.close();
   907         if (taskListener != null)
   908             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
   910         if (errorStatus) {
   911             compiler.log.nerrors += messager.errorCount();
   912             if (compiler.errorCount() == 0)
   913                 compiler.log.nerrors++;
   914         } else if (procOnly) {
   915             compiler.todo.clear();
   916         } else { // Final compilation
   917             compiler.close(false);
   918             currentContext = contextForNextRound(currentContext, true);
   919             compiler = JavaCompiler.instance(currentContext);
   921             if (true) {
   922                 compiler.enterTrees(cleanTrees(roots));
   923             } else {
   924                 List<JavaFileObject> fileObjects = List.nil();
   925                 for (JCCompilationUnit unit : roots)
   926                     fileObjects = fileObjects.prepend(unit.getSourceFile());
   927                 roots = null;
   928                 compiler.enterTrees(compiler.parseFiles(fileObjects.reverse()));
   929             }
   930         }
   932         return compiler;
   933     }
   935     // Call the last round of annotation processing
   936     private void runLastRound(PrintWriter xout,
   937                               int roundNumber,
   938                               boolean errorStatus,
   939                               TaskListener taskListener) throws IOException {
   940         roundNumber++;
   941         List<ClassSymbol> noTopLevelClasses = List.nil();
   942         Set<TypeElement> noAnnotations =  Collections.emptySet();
   943         printRoundInfo(xout, roundNumber, noTopLevelClasses, noAnnotations, true);
   945         Set<Element> emptyRootElements = Collections.emptySet(); // immutable
   946         RoundEnvironment renv = new JavacRoundEnvironment(true,
   947                                                           errorStatus,
   948                                                           emptyRootElements,
   949                                                           JavacProcessingEnvironment.this);
   950         if (taskListener != null)
   951             taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   953         try {
   954             discoveredProcs.iterator().runContributingProcs(renv);
   955         } finally {
   956             if (taskListener != null)
   957                 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   958         }
   959     }
   961     private void updateProcessingState(Context currentContext, boolean lastRound) {
   962         filer.newRound(currentContext, lastRound);
   963         messager.newRound(currentContext);
   965         elementUtils.setContext(currentContext);
   966         typeUtils.setContext(currentContext);
   967     }
   969     private void warnIfUnmatchedOptions() {
   970         if (!unmatchedProcessorOptions.isEmpty()) {
   971             log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
   972         }
   973     }
   975     private void printRoundInfo(PrintWriter xout,
   976                                 int roundNumber,
   977                                 List<ClassSymbol> topLevelClasses,
   978                                 Set<TypeElement> annotationsPresent,
   979                                 boolean lastRound) {
   980         if (printRounds || verbose) {
   981             xout.println(Log.getLocalizedString("x.print.rounds",
   982                                                 roundNumber,
   983                                                 "{" + topLevelClasses.toString(", ") + "}",
   984                                                 annotationsPresent,
   985                                                 lastRound));
   986         }
   987     }
   989     private ListBuffer<ClassSymbol> enterNewClassFiles(Context currentContext) {
   990         ClassReader reader = ClassReader.instance(currentContext);
   991         Names names = Names.instance(currentContext);
   992         ListBuffer<ClassSymbol> list = new ListBuffer<ClassSymbol>();
   994         for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) {
   995             Name name = names.fromString(entry.getKey());
   996             JavaFileObject file = entry.getValue();
   997             if (file.getKind() != JavaFileObject.Kind.CLASS)
   998                 throw new AssertionError(file);
   999             ClassSymbol cs = reader.enterClass(name, file);
  1000             list.append(cs);
  1002         return list;
  1005     /**
  1006      * Free resources related to annotation processing.
  1007      */
  1008     public void close() {
  1009         filer.close();
  1010         discoveredProcs = null;
  1013     private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
  1014         List<ClassSymbol> classes = List.nil();
  1015         for (JCCompilationUnit unit : units) {
  1016             for (JCTree node : unit.defs) {
  1017                 if (node.getTag() == JCTree.CLASSDEF) {
  1018                     classes = classes.prepend(((JCClassDecl) node).sym);
  1022         return classes.reverse();
  1025     private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) {
  1026         List<PackageSymbol> packages = List.nil();
  1027         for (JCCompilationUnit unit : units) {
  1028             boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info",
  1029                                                                  JavaFileObject.Kind.SOURCE);
  1030             if (isPkgInfo) {
  1031                 packages = packages.prepend(unit.packge);
  1034         return packages.reverse();
  1037     private Context contextForNextRound(Context context, boolean shareNames)
  1038         throws IOException
  1040         Context next = new Context();
  1042         Options options = Options.instance(context);
  1043         assert options != null;
  1044         next.put(Options.optionsKey, options);
  1046         PrintWriter out = context.get(Log.outKey);
  1047         assert out != null;
  1048         next.put(Log.outKey, out);
  1050         if (shareNames) {
  1051             Names names = Names.instance(context);
  1052             assert names != null;
  1053             next.put(Names.namesKey, names);
  1056         DiagnosticListener dl = context.get(DiagnosticListener.class);
  1057         if (dl != null)
  1058             next.put(DiagnosticListener.class, dl);
  1060         TaskListener tl = context.get(TaskListener.class);
  1061         if (tl != null)
  1062             next.put(TaskListener.class, tl);
  1064         JavaFileManager jfm = context.get(JavaFileManager.class);
  1065         assert jfm != null;
  1066         next.put(JavaFileManager.class, jfm);
  1067         if (jfm instanceof JavacFileManager) {
  1068             ((JavacFileManager)jfm).setContext(next);
  1071         Names names = Names.instance(context);
  1072         assert names != null;
  1073         next.put(Names.namesKey, names);
  1075         Keywords keywords = Keywords.instance(context);
  1076         assert(keywords != null);
  1077         next.put(Keywords.keywordsKey, keywords);
  1079         JavaCompiler oldCompiler = JavaCompiler.instance(context);
  1080         JavaCompiler nextCompiler = JavaCompiler.instance(next);
  1081         nextCompiler.initRound(oldCompiler);
  1083         JavacTaskImpl task = context.get(JavacTaskImpl.class);
  1084         if (task != null) {
  1085             next.put(JavacTaskImpl.class, task);
  1086             task.updateContext(next);
  1089         context.clear();
  1090         return next;
  1093     /*
  1094      * Called retroactively to determine if a class loader was required,
  1095      * after we have failed to create one.
  1096      */
  1097     private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) {
  1098         if (procNames != null)
  1099             return true;
  1101         String procPath;
  1102         URL[] urls = new URL[1];
  1103         for(File pathElement : workingpath) {
  1104             try {
  1105                 urls[0] = pathElement.toURI().toURL();
  1106                 if (ServiceProxy.hasService(Processor.class, urls))
  1107                     return true;
  1108             } catch (MalformedURLException ex) {
  1109                 throw new AssertionError(ex);
  1111             catch (ServiceProxy.ServiceConfigurationError e) {
  1112                 log.error("proc.bad.config.file", e.getLocalizedMessage());
  1113                 return true;
  1117         return false;
  1120     private class AnnotationCollector extends TreeScanner {
  1121         List<JCTree> path = List.nil();
  1122         static final boolean verbose = false;
  1123         List<JCAnnotation> annotations = List.nil();
  1125         public List<JCAnnotation> findAnnotations(List<? extends JCTree> nodes) {
  1126             annotations = List.nil();
  1127             scan(nodes);
  1128             List<JCAnnotation> found = annotations;
  1129             annotations = List.nil();
  1130             return found.reverse();
  1133         public void scan(JCTree node) {
  1134             if (node == null)
  1135                 return;
  1136             Symbol sym = TreeInfo.symbolFor(node);
  1137             if (sym != null)
  1138                 path = path.prepend(node);
  1139             super.scan(node);
  1140             if (sym != null)
  1141                 path = path.tail;
  1144         public void visitAnnotation(JCAnnotation node) {
  1145             annotations = annotations.prepend(node);
  1146             if (verbose) {
  1147                 StringBuilder sb = new StringBuilder();
  1148                 for (JCTree tree : path.reverse()) {
  1149                     System.err.print(sb);
  1150                     System.err.println(TreeInfo.symbolFor(tree));
  1151                     sb.append("  ");
  1153                 System.err.print(sb);
  1154                 System.err.println(node);
  1159     private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
  1160         for (T node : nodes)
  1161             treeCleaner.scan(node);
  1162         return nodes;
  1165     private static TreeScanner treeCleaner = new TreeScanner() {
  1166             public void scan(JCTree node) {
  1167                 super.scan(node);
  1168                 if (node != null)
  1169                     node.type = null;
  1171             public void visitTopLevel(JCCompilationUnit node) {
  1172                 node.packge = null;
  1173                 super.visitTopLevel(node);
  1175             public void visitClassDef(JCClassDecl node) {
  1176                 node.sym = null;
  1177                 super.visitClassDef(node);
  1179             public void visitMethodDef(JCMethodDecl node) {
  1180                 node.sym = null;
  1181                 super.visitMethodDef(node);
  1183             public void visitVarDef(JCVariableDecl node) {
  1184                 node.sym = null;
  1185                 super.visitVarDef(node);
  1187             public void visitNewClass(JCNewClass node) {
  1188                 node.constructor = null;
  1189                 super.visitNewClass(node);
  1191             public void visitAssignop(JCAssignOp node) {
  1192                 node.operator = null;
  1193                 super.visitAssignop(node);
  1195             public void visitUnary(JCUnary node) {
  1196                 node.operator = null;
  1197                 super.visitUnary(node);
  1199             public void visitBinary(JCBinary node) {
  1200                 node.operator = null;
  1201                 super.visitBinary(node);
  1203             public void visitSelect(JCFieldAccess node) {
  1204                 node.sym = null;
  1205                 super.visitSelect(node);
  1207             public void visitIdent(JCIdent node) {
  1208                 node.sym = null;
  1209                 super.visitIdent(node);
  1211         };
  1214     private boolean moreToDo() {
  1215         return filer.newFiles();
  1218     /**
  1219      * {@inheritdoc}
  1221      * Command line options suitable for presenting to annotation
  1222      * processors.  "-Afoo=bar" should be "-Afoo" => "bar".
  1223      */
  1224     public Map<String,String> getOptions() {
  1225         return processorOptions;
  1228     public Messager getMessager() {
  1229         return messager;
  1232     public Filer getFiler() {
  1233         return filer;
  1236     public JavacElements getElementUtils() {
  1237         return elementUtils;
  1240     public JavacTypes getTypeUtils() {
  1241         return typeUtils;
  1244     public SourceVersion getSourceVersion() {
  1245         return Source.toSourceVersion(source);
  1248     public Locale getLocale() {
  1249         return Locale.getDefault();
  1252     public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
  1253         return specifiedPackages;
  1256     // Borrowed from DocletInvoker and apt
  1257     // TODO: remove from apt's Main
  1258     /**
  1259      * Utility method for converting a search path string to an array
  1260      * of directory and JAR file URLs.
  1262      * @param path the search path string
  1263      * @return the resulting array of directory and JAR file URLs
  1264      */
  1265     public static URL[] pathToURLs(String path) {
  1266         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  1267         URL[] urls = new URL[st.countTokens()];
  1268         int count = 0;
  1269         while (st.hasMoreTokens()) {
  1270             URL url = fileToURL(new File(st.nextToken()));
  1271             if (url != null) {
  1272                 urls[count++] = url;
  1275         if (urls.length != count) {
  1276             URL[] tmp = new URL[count];
  1277             System.arraycopy(urls, 0, tmp, 0, count);
  1278             urls = tmp;
  1280         return urls;
  1283     /**
  1284      * Returns the directory or JAR file URL corresponding to the specified
  1285      * local file name.
  1287      * @param file the File object
  1288      * @return the resulting directory or JAR file URL, or null if unknown
  1289      */
  1290     private static URL fileToURL(File file) {
  1291         String name;
  1292         try {
  1293             name = file.getCanonicalPath();
  1294         } catch (IOException e) {
  1295             name = file.getAbsolutePath();
  1297         name = name.replace(File.separatorChar, '/');
  1298         if (!name.startsWith("/")) {
  1299             name = "/" + name;
  1301         // If the file does not exist, then assume that it's a directory
  1302         if (!file.isFile()) {
  1303             name = name + "/";
  1305         try {
  1306             return new URL("file", "", name);
  1307         } catch (MalformedURLException e) {
  1308             throw new IllegalArgumentException("file");
  1314     private static final Pattern allMatches = Pattern.compile(".*");
  1316     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
  1317     /**
  1318      * Convert import-style string to regex matching that string.  If
  1319      * the string is a valid import-style string, return a regex that
  1320      * won't match anything.
  1321      */
  1322     // TODO: remove version in Apt.java
  1323     public static Pattern importStringToPattern(String s, Processor p, Log log) {
  1324         if (s.equals("*")) {
  1325             return allMatches;
  1326         } else {
  1327             String t = s;
  1328             boolean star = false;
  1330             /*
  1331              * Validate string from factory is legal.  If the string
  1332              * has more than one asterisks or the asterisks does not
  1333              * appear as the last character (preceded by a period),
  1334              * the string is not legal.
  1335              */
  1337             boolean valid = true;
  1338             int index = t.indexOf('*');
  1339             if (index != -1) {
  1340                 // '*' must be last character...
  1341                 if (index == t.length() -1) {
  1342                      // ... and preceeding character must be '.'
  1343                     if ( index-1 >= 0 ) {
  1344                         valid = t.charAt(index-1) == '.';
  1345                         // Strip off ".*$" for identifier checks
  1346                         t = t.substring(0, t.length()-2);
  1348                 } else
  1349                     valid = false;
  1352             // Verify string is off the form (javaId \.)+ or javaId
  1353             if (valid) {
  1354                 String[] javaIds = t.split("\\.", t.length()+2);
  1355                 for(String javaId: javaIds)
  1356                     valid &= SourceVersion.isIdentifier(javaId);
  1359             if (!valid) {
  1360                 log.warning("proc.malformed.supported.string", s, p.getClass().getName());
  1361                 return noMatches; // won't match any valid identifier
  1364             String s_prime = s.replaceAll("\\.", "\\\\.");
  1366             if (s_prime.endsWith("*")) {
  1367                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
  1370             return Pattern.compile(s_prime);
  1374     /**
  1375      * For internal use by Sun Microsystems only.  This method will be
  1376      * removed without warning.
  1377      */
  1378     public Context getContext() {
  1379         return context;
  1382     public String toString() {
  1383         return "javac ProcessingEnvironment";
  1386     public static boolean isValidOptionName(String optionName) {
  1387         for(String s : optionName.split("\\.", -1)) {
  1388             if (!SourceVersion.isIdentifier(s))
  1389                 return false;
  1391         return true;

mercurial