src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java

Tue, 28 Feb 2012 10:33:49 -0800

author
jjg
date
Tue, 28 Feb 2012 10:33:49 -0800
changeset 1210
62e611704863
parent 1187
ac36176b7de0
child 1413
bdcef2ef52d2
permissions
-rw-r--r--

7093891: support multiple task listeners
Reviewed-by: darcy, mcimadamore

     1 /*
     2  * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.tools.javac.api;
    28 import java.io.File;
    29 import java.io.IOException;
    30 import java.nio.CharBuffer;
    31 import java.util.*;
    32 import java.util.concurrent.atomic.AtomicBoolean;
    34 import javax.annotation.processing.Processor;
    35 import javax.lang.model.element.Element;
    36 import javax.lang.model.element.TypeElement;
    37 import javax.lang.model.type.TypeMirror;
    38 import javax.tools.*;
    40 import com.sun.source.tree.*;
    41 import com.sun.source.util.*;
    42 import com.sun.tools.javac.code.*;
    43 import com.sun.tools.javac.code.Symbol.*;
    44 import com.sun.tools.javac.comp.*;
    45 import com.sun.tools.javac.file.JavacFileManager;
    46 import com.sun.tools.javac.main.*;
    47 import com.sun.tools.javac.main.JavaCompiler;
    48 import com.sun.tools.javac.model.*;
    49 import com.sun.tools.javac.parser.Parser;
    50 import com.sun.tools.javac.parser.ParserFactory;
    51 import com.sun.tools.javac.tree.*;
    52 import com.sun.tools.javac.tree.JCTree.*;
    53 import com.sun.tools.javac.util.*;
    54 import com.sun.tools.javac.util.List;
    56 /**
    57  * Provides access to functionality specific to the JDK Java Compiler, javac.
    58  *
    59  * <p><b>This is NOT part of any supported API.
    60  * If you write code that depends on this, you do so at your own
    61  * risk.  This code and its internal interfaces are subject to change
    62  * or deletion without notice.</b></p>
    63  *
    64  * @author Peter von der Ah&eacute;
    65  * @author Jonathan Gibbons
    66  */
    67 public class JavacTaskImpl extends BasicJavacTask {
    68     private ClientCodeWrapper ccw;
    69     private Main compilerMain;
    70     private JavaCompiler compiler;
    71     private Locale locale;
    72     private String[] args;
    73     private String[] classNames;
    74     private List<JavaFileObject> fileObjects;
    75     private Map<JavaFileObject, JCCompilationUnit> notYetEntered;
    76     private ListBuffer<Env<AttrContext>> genList;
    77     private AtomicBoolean used = new AtomicBoolean();
    78     private Iterable<? extends Processor> processors;
    80     private Main.Result result = null;
    82     JavacTaskImpl(Main compilerMain,
    83                 String[] args,
    84                 String[] classNames,
    85                 Context context,
    86                 List<JavaFileObject> fileObjects) {
    87         super(null, false);
    88         this.ccw = ClientCodeWrapper.instance(context);
    89         this.compilerMain = compilerMain;
    90         this.args = args;
    91         this.classNames = classNames;
    92         this.context = context;
    93         this.fileObjects = fileObjects;
    94         setLocale(Locale.getDefault());
    95         // null checks
    96         compilerMain.getClass();
    97         args.getClass();
    98         fileObjects.getClass();
    99     }
   101     JavacTaskImpl(Main compilerMain,
   102                 Iterable<String> flags,
   103                 Context context,
   104                 Iterable<String> classes,
   105                 Iterable<? extends JavaFileObject> fileObjects) {
   106         this(compilerMain, toArray(flags), toArray(classes), context, toList(fileObjects));
   107     }
   109     static private String[] toArray(Iterable<String> iter) {
   110         ListBuffer<String> result = new ListBuffer<String>();
   111         if (iter != null)
   112             for (String s : iter)
   113                 result.append(s);
   114         return result.toArray(new String[result.length()]);
   115     }
   117     static private List<JavaFileObject> toList(Iterable<? extends JavaFileObject> fileObjects) {
   118         if (fileObjects == null)
   119             return List.nil();
   120         ListBuffer<JavaFileObject> result = new ListBuffer<JavaFileObject>();
   121         for (JavaFileObject fo : fileObjects)
   122             result.append(fo);
   123         return result.toList();
   124     }
   126     public Boolean call() {
   127         if (!used.getAndSet(true)) {
   128             initContext();
   129             notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>();
   130             compilerMain.setAPIMode(true);
   131             result = compilerMain.compile(args, classNames, context, fileObjects, processors);
   132             cleanup();
   133             return result.isOK();
   134         } else {
   135             throw new IllegalStateException("multiple calls to method 'call'");
   136         }
   137     }
   139     public void setProcessors(Iterable<? extends Processor> processors) {
   140         processors.getClass(); // null check
   141         // not mt-safe
   142         if (used.get())
   143             throw new IllegalStateException();
   144         this.processors = processors;
   145     }
   147     public void setLocale(Locale locale) {
   148         if (used.get())
   149             throw new IllegalStateException();
   150         this.locale = locale;
   151     }
   153     private void prepareCompiler() throws IOException {
   154         if (used.getAndSet(true)) {
   155             if (compiler == null)
   156                 throw new IllegalStateException();
   157         } else {
   158             initContext();
   159             compilerMain.setOptions(Options.instance(context));
   160             compilerMain.filenames = new LinkedHashSet<File>();
   161             Collection<File> filenames = compilerMain.processArgs(CommandLine.parse(args), classNames);
   162             if (!filenames.isEmpty())
   163                 throw new IllegalArgumentException("Malformed arguments " + toString(filenames, " "));
   164             compiler = JavaCompiler.instance(context);
   165             compiler.keepComments = true;
   166             compiler.genEndPos = true;
   167             // NOTE: this value will be updated after annotation processing
   168             compiler.initProcessAnnotations(processors);
   169             notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>();
   170             for (JavaFileObject file: fileObjects)
   171                 notYetEntered.put(file, null);
   172             genList = new ListBuffer<Env<AttrContext>>();
   173             // endContext will be called when all classes have been generated
   174             // TODO: should handle the case after each phase if errors have occurred
   175             args = null;
   176             classNames = null;
   177         }
   178     }
   180     <T> String toString(Iterable<T> items, String sep) {
   181         String currSep = "";
   182         StringBuilder sb = new StringBuilder();
   183         for (T item: items) {
   184             sb.append(currSep);
   185             sb.append(item.toString());
   186             currSep = sep;
   187         }
   188         return sb.toString();
   189     }
   191     private void initContext() {
   192         context.put(JavacTask.class, this);
   193         //initialize compiler's default locale
   194         context.put(Locale.class, locale);
   195     }
   197     void cleanup() {
   198         if (compiler != null)
   199             compiler.close();
   200         compiler = null;
   201         compilerMain = null;
   202         args = null;
   203         classNames = null;
   204         context = null;
   205         fileObjects = null;
   206         notYetEntered = null;
   207     }
   209     /**
   210      * Construct a JavaFileObject from the given file.
   211      *
   212      * <p><b>TODO: this method is useless here</b></p>
   213      *
   214      * @param file a file
   215      * @return a JavaFileObject from the standard file manager.
   216      */
   217     public JavaFileObject asJavaFileObject(File file) {
   218         JavacFileManager fm = (JavacFileManager)context.get(JavaFileManager.class);
   219         return fm.getRegularFile(file);
   220     }
   222     /**
   223      * Parse the specified files returning a list of abstract syntax trees.
   224      *
   225      * @throws java.io.IOException TODO
   226      * @return a list of abstract syntax trees
   227      */
   228     public Iterable<? extends CompilationUnitTree> parse() throws IOException {
   229         try {
   230             prepareCompiler();
   231             List<JCCompilationUnit> units = compiler.parseFiles(fileObjects);
   232             for (JCCompilationUnit unit: units) {
   233                 JavaFileObject file = unit.getSourceFile();
   234                 if (notYetEntered.containsKey(file))
   235                     notYetEntered.put(file, unit);
   236             }
   237             return units;
   238         }
   239         finally {
   240             parsed = true;
   241             if (compiler != null && compiler.log != null)
   242                 compiler.log.flush();
   243         }
   244     }
   246     private boolean parsed = false;
   248     /**
   249      * Translate all the abstract syntax trees to elements.
   250      *
   251      * @throws IOException TODO
   252      * @return a list of elements corresponding to the top level
   253      * classes in the abstract syntax trees
   254      */
   255     public Iterable<? extends TypeElement> enter() throws IOException {
   256         return enter(null);
   257     }
   259     /**
   260      * Translate the given abstract syntax trees to elements.
   261      *
   262      * @param trees a list of abstract syntax trees.
   263      * @throws java.io.IOException TODO
   264      * @return a list of elements corresponding to the top level
   265      * classes in the abstract syntax trees
   266      */
   267     public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees)
   268         throws IOException
   269     {
   270         if (trees == null && notYetEntered != null && notYetEntered.isEmpty())
   271             return List.nil();
   273         prepareCompiler();
   275         ListBuffer<JCCompilationUnit> roots = null;
   277         if (trees == null) {
   278             // If there are still files which were specified to be compiled
   279             // (i.e. in fileObjects) but which have not yet been entered,
   280             // then we make sure they have been parsed and add them to the
   281             // list to be entered.
   282             if (notYetEntered.size() > 0) {
   283                 if (!parsed)
   284                     parse(); // TODO would be nice to specify files needed to be parsed
   285                 for (JavaFileObject file: fileObjects) {
   286                     JCCompilationUnit unit = notYetEntered.remove(file);
   287                     if (unit != null) {
   288                         if (roots == null)
   289                             roots = new ListBuffer<JCCompilationUnit>();
   290                         roots.append(unit);
   291                     }
   292                 }
   293                 notYetEntered.clear();
   294             }
   295         }
   296         else {
   297             for (CompilationUnitTree cu : trees) {
   298                 if (cu instanceof JCCompilationUnit) {
   299                     if (roots == null)
   300                         roots = new ListBuffer<JCCompilationUnit>();
   301                     roots.append((JCCompilationUnit)cu);
   302                     notYetEntered.remove(cu.getSourceFile());
   303                 }
   304                 else
   305                     throw new IllegalArgumentException(cu.toString());
   306             }
   307         }
   309         if (roots == null)
   310             return List.nil();
   312         try {
   313             List<JCCompilationUnit> units = compiler.enterTrees(roots.toList());
   315             if (notYetEntered.isEmpty())
   316                 compiler = compiler.processAnnotations(units);
   318             ListBuffer<TypeElement> elements = new ListBuffer<TypeElement>();
   319             for (JCCompilationUnit unit : units) {
   320                 for (JCTree node : unit.defs) {
   321                     if (node.hasTag(JCTree.Tag.CLASSDEF)) {
   322                         JCClassDecl cdef = (JCClassDecl) node;
   323                         if (cdef.sym != null) // maybe null if errors in anno processing
   324                             elements.append(cdef.sym);
   325                     }
   326                 }
   327             }
   328             return elements.toList();
   329         }
   330         finally {
   331             compiler.log.flush();
   332         }
   333     }
   335     /**
   336      * Complete all analysis.
   337      * @throws IOException TODO
   338      */
   339     @Override
   340     public Iterable<? extends Element> analyze() throws IOException {
   341         return analyze(null);
   342     }
   344     /**
   345      * Complete all analysis on the given classes.
   346      * This can be used to ensure that all compile time errors are reported.
   347      * The classes must have previously been returned from {@link #enter}.
   348      * If null is specified, all outstanding classes will be analyzed.
   349      *
   350      * @param classes a list of class elements
   351      */
   352     // This implementation requires that we open up privileges on JavaCompiler.
   353     // An alternative implementation would be to move this code to JavaCompiler and
   354     // wrap it here
   355     public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) throws IOException {
   356         enter(null);  // ensure all classes have been entered
   358         final ListBuffer<Element> results = new ListBuffer<Element>();
   359         try {
   360             if (classes == null) {
   361                 handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results);
   362             } else {
   363                 Filter f = new Filter() {
   364                     public void process(Env<AttrContext> env) {
   365                         handleFlowResults(compiler.flow(compiler.attribute(env)), results);
   366                     }
   367                 };
   368                 f.run(compiler.todo, classes);
   369             }
   370         } finally {
   371             compiler.log.flush();
   372         }
   373         return results;
   374     }
   375     // where
   376         private void handleFlowResults(Queue<Env<AttrContext>> queue, ListBuffer<Element> elems) {
   377             for (Env<AttrContext> env: queue) {
   378                 switch (env.tree.getTag()) {
   379                     case CLASSDEF:
   380                         JCClassDecl cdef = (JCClassDecl) env.tree;
   381                         if (cdef.sym != null)
   382                             elems.append(cdef.sym);
   383                         break;
   384                     case TOPLEVEL:
   385                         JCCompilationUnit unit = (JCCompilationUnit) env.tree;
   386                         if (unit.packge != null)
   387                             elems.append(unit.packge);
   388                         break;
   389                 }
   390             }
   391             genList.addAll(queue);
   392         }
   395     /**
   396      * Generate code.
   397      * @throws IOException TODO
   398      */
   399     @Override
   400     public Iterable<? extends JavaFileObject> generate() throws IOException {
   401         return generate(null);
   402     }
   404     /**
   405      * Generate code corresponding to the given classes.
   406      * The classes must have previously been returned from {@link #enter}.
   407      * If there are classes outstanding to be analyzed, that will be done before
   408      * any classes are generated.
   409      * If null is specified, code will be generated for all outstanding classes.
   410      *
   411      * @param classes a list of class elements
   412      */
   413     public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) throws IOException {
   414         final ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
   415         try {
   416             analyze(null);  // ensure all classes have been parsed, entered, and analyzed
   418             if (classes == null) {
   419                 compiler.generate(compiler.desugar(genList), results);
   420                 genList.clear();
   421             }
   422             else {
   423                 Filter f = new Filter() {
   424                         public void process(Env<AttrContext> env) {
   425                             compiler.generate(compiler.desugar(ListBuffer.of(env)), results);
   426                         }
   427                     };
   428                 f.run(genList, classes);
   429             }
   430             if (genList.isEmpty()) {
   431                 compiler.reportDeferredDiagnostics();
   432                 cleanup();
   433             }
   434         }
   435         finally {
   436             if (compiler != null)
   437                 compiler.log.flush();
   438         }
   439         return results;
   440     }
   442     public TypeMirror getTypeMirror(Iterable<? extends Tree> path) {
   443         // TODO: Should complete attribution if necessary
   444         Tree last = null;
   445         for (Tree node : path)
   446             last = node;
   447         return ((JCTree)last).type;
   448     }
   450     public JavacElements getElements() {
   451         if (context == null)
   452             throw new IllegalStateException();
   453         return JavacElements.instance(context);
   454     }
   456     public JavacTypes getTypes() {
   457         if (context == null)
   458             throw new IllegalStateException();
   459         return JavacTypes.instance(context);
   460     }
   462     public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) {
   463         return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse();
   464     }
   466     abstract class Filter {
   467         void run(Queue<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) {
   468             Set<TypeElement> set = new HashSet<TypeElement>();
   469             for (TypeElement item: classes)
   470                 set.add(item);
   472             ListBuffer<Env<AttrContext>> defer = ListBuffer.<Env<AttrContext>>lb();
   473             while (list.peek() != null) {
   474                 Env<AttrContext> env = list.remove();
   475                 ClassSymbol csym = env.enclClass.sym;
   476                 if (csym != null && set.contains(csym.outermostClass()))
   477                     process(env);
   478                 else
   479                     defer = defer.append(env);
   480             }
   482             list.addAll(defer);
   483         }
   485         abstract void process(Env<AttrContext> env);
   486     }
   488     /**
   489      * For internal use only.  This method will be
   490      * removed without warning.
   491      */
   492     public Context getContext() {
   493         return context;
   494     }
   496     /**
   497      * For internal use only.  This method will be
   498      * removed without warning.
   499      */
   500     public void updateContext(Context newContext) {
   501         context = newContext;
   502     }
   504     /**
   505      * For internal use only.  This method will be
   506      * removed without warning.
   507      */
   508     public Type parseType(String expr, TypeElement scope) {
   509         if (expr == null || expr.equals(""))
   510             throw new IllegalArgumentException();
   511         compiler = JavaCompiler.instance(context);
   512         JavaFileObject prev = compiler.log.useSource(null);
   513         ParserFactory parserFactory = ParserFactory.instance(context);
   514         Attr attr = Attr.instance(context);
   515         try {
   516             CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length());
   517             Parser parser = parserFactory.newParser(buf, false, false, false);
   518             JCTree tree = parser.parseType();
   519             return attr.attribType(tree, (Symbol.TypeSymbol)scope);
   520         } finally {
   521             compiler.log.useSource(prev);
   522         }
   523     }
   525 }

mercurial