src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java
changeset 47216 71c04702a3d5
parent 43871 f164f4506389
child 49197 cc2673fa8c20
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2005, 2017, 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  */
       
    25 
       
    26 package com.sun.tools.javac.api;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.nio.CharBuffer;
       
    30 import java.util.*;
       
    31 import java.util.concurrent.Callable;
       
    32 import java.util.concurrent.atomic.AtomicBoolean;
       
    33 
       
    34 import javax.annotation.processing.Processor;
       
    35 import javax.lang.model.element.Element;
       
    36 import javax.lang.model.element.TypeElement;
       
    37 import javax.tools.*;
       
    38 
       
    39 import com.sun.source.tree.*;
       
    40 import com.sun.tools.javac.code.*;
       
    41 import com.sun.tools.javac.code.Symbol.ClassSymbol;
       
    42 import com.sun.tools.javac.comp.*;
       
    43 import com.sun.tools.javac.file.BaseFileManager;
       
    44 import com.sun.tools.javac.main.*;
       
    45 import com.sun.tools.javac.main.JavaCompiler;
       
    46 import com.sun.tools.javac.parser.Parser;
       
    47 import com.sun.tools.javac.parser.ParserFactory;
       
    48 import com.sun.tools.javac.processing.AnnotationProcessingError;
       
    49 import com.sun.tools.javac.tree.*;
       
    50 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
       
    51 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
       
    52 import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
       
    53 import com.sun.tools.javac.tree.JCTree.Tag;
       
    54 import com.sun.tools.javac.util.*;
       
    55 import com.sun.tools.javac.util.DefinedBy.Api;
       
    56 import com.sun.tools.javac.util.List;
       
    57 import com.sun.tools.javac.util.Log.PrefixKind;
       
    58 import com.sun.tools.javac.util.Log.WriterKind;
       
    59 
       
    60 /**
       
    61  * Provides access to functionality specific to the JDK Java Compiler, javac.
       
    62  *
       
    63  * <p><b>This is NOT part of any supported API.
       
    64  * If you write code that depends on this, you do so at your own
       
    65  * risk.  This code and its internal interfaces are subject to change
       
    66  * or deletion without notice.</b></p>
       
    67  *
       
    68  * @author Peter von der Ah&eacute;
       
    69  * @author Jonathan Gibbons
       
    70  */
       
    71 public class JavacTaskImpl extends BasicJavacTask {
       
    72     private final Arguments args;
       
    73     private JavaCompiler compiler;
       
    74     private JavaFileManager fileManager;
       
    75     private Locale locale;
       
    76     private Map<JavaFileObject, JCCompilationUnit> notYetEntered;
       
    77     private ListBuffer<Env<AttrContext>> genList;
       
    78     private final AtomicBoolean used = new AtomicBoolean();
       
    79     private Iterable<? extends Processor> processors;
       
    80     private ListBuffer<String> addModules = new ListBuffer<>();
       
    81 
       
    82     protected JavacTaskImpl(Context context) {
       
    83         super(context, true);
       
    84         args = Arguments.instance(context);
       
    85         fileManager = context.get(JavaFileManager.class);
       
    86     }
       
    87 
       
    88     @Override @DefinedBy(Api.COMPILER)
       
    89     public Boolean call() {
       
    90         return doCall().isOK();
       
    91     }
       
    92 
       
    93     /* Internal version of call exposing Main.Result. */
       
    94     public Main.Result doCall() {
       
    95         try {
       
    96             return handleExceptions(() -> {
       
    97                 prepareCompiler(false);
       
    98                 if (compiler.errorCount() > 0)
       
    99                     return Main.Result.ERROR;
       
   100                 compiler.compile(args.getFileObjects(), args.getClassNames(), processors, addModules);
       
   101                 return (compiler.errorCount() > 0) ? Main.Result.ERROR : Main.Result.OK; // FIXME?
       
   102             }, Main.Result.SYSERR, Main.Result.ABNORMAL);
       
   103         } finally {
       
   104             try {
       
   105                 cleanup();
       
   106             } catch (ClientCodeException e) {
       
   107                 throw new RuntimeException(e.getCause());
       
   108             }
       
   109         }
       
   110     }
       
   111 
       
   112     @Override @DefinedBy(Api.COMPILER)
       
   113     public void addModules(Iterable<String> moduleNames) {
       
   114         Objects.requireNonNull(moduleNames);
       
   115         // not mt-safe
       
   116         if (used.get())
       
   117             throw new IllegalStateException();
       
   118         for (String m : moduleNames) {
       
   119             Objects.requireNonNull(m);
       
   120             addModules.add(m);
       
   121         }
       
   122     }
       
   123 
       
   124     @Override @DefinedBy(Api.COMPILER)
       
   125     public void setProcessors(Iterable<? extends Processor> processors) {
       
   126         Objects.requireNonNull(processors);
       
   127         // not mt-safe
       
   128         if (used.get())
       
   129             throw new IllegalStateException();
       
   130         this.processors = processors;
       
   131     }
       
   132 
       
   133     @Override @DefinedBy(Api.COMPILER)
       
   134     public void setLocale(Locale locale) {
       
   135         if (used.get())
       
   136             throw new IllegalStateException();
       
   137         this.locale = locale;
       
   138     }
       
   139 
       
   140     private <T> T handleExceptions(Callable<T> c, T sysErrorResult, T abnormalErrorResult) {
       
   141         try {
       
   142             return c.call();
       
   143         } catch (FatalError ex) {
       
   144             Log log = Log.instance(context);
       
   145             Options options = Options.instance(context);
       
   146             log.printRawLines(ex.getMessage());
       
   147             if (ex.getCause() != null && options.isSet("dev")) {
       
   148                 ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE));
       
   149             }
       
   150             return sysErrorResult;
       
   151         } catch (AnnotationProcessingError | ClientCodeException e) {
       
   152             // AnnotationProcessingError is thrown from JavacProcessingEnvironment,
       
   153             // to forward errors thrown from an annotation processor
       
   154             // ClientCodeException is thrown from ClientCodeWrapper,
       
   155             // to forward errors thrown from user-supplied code for Compiler API
       
   156             // as specified by javax.tools.JavaCompiler#getTask
       
   157             // and javax.tools.JavaCompiler.CompilationTask#call
       
   158             throw new RuntimeException(e.getCause());
       
   159         } catch (PropagatedException e) {
       
   160             throw e.getCause();
       
   161         } catch (IllegalStateException e) {
       
   162             throw e;
       
   163         } catch (Exception | Error ex) {
       
   164             // Nasty.  If we've already reported an error, compensate
       
   165             // for buggy compiler error recovery by swallowing thrown
       
   166             // exceptions.
       
   167             if (compiler == null || compiler.errorCount() == 0
       
   168                     || Options.instance(context).isSet("dev")) {
       
   169                 Log log = Log.instance(context);
       
   170                 log.printLines(PrefixKind.JAVAC, "msg.bug", JavaCompiler.version());
       
   171                 ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
       
   172             }
       
   173             return abnormalErrorResult;
       
   174         }
       
   175     }
       
   176 
       
   177     private void prepareCompiler(boolean forParse) {
       
   178         if (used.getAndSet(true)) {
       
   179             if (compiler == null)
       
   180                 throw new PropagatedException(new IllegalStateException());
       
   181         } else {
       
   182             args.validate();
       
   183 
       
   184             //initialize compiler's default locale
       
   185             context.put(Locale.class, locale);
       
   186 
       
   187             // hack
       
   188             JavacMessages messages = context.get(JavacMessages.messagesKey);
       
   189             if (messages != null && !messages.getCurrentLocale().equals(locale))
       
   190                 messages.setCurrentLocale(locale);
       
   191 
       
   192             initPlugins(args.getPluginOpts());
       
   193             initDocLint(args.getDocLintOpts());
       
   194 
       
   195             // init JavaCompiler and queues
       
   196             compiler = JavaCompiler.instance(context);
       
   197             compiler.keepComments = true;
       
   198             compiler.genEndPos = true;
       
   199             notYetEntered = new HashMap<>();
       
   200             if (forParse) {
       
   201                 compiler.initProcessAnnotations(processors, args.getFileObjects(), args.getClassNames());
       
   202                 for (JavaFileObject file: args.getFileObjects())
       
   203                     notYetEntered.put(file, null);
       
   204                 genList = new ListBuffer<>();
       
   205             }
       
   206         }
       
   207     }
       
   208 
       
   209     <T> String toString(Iterable<T> items, String sep) {
       
   210         String currSep = "";
       
   211         StringBuilder sb = new StringBuilder();
       
   212         for (T item: items) {
       
   213             sb.append(currSep);
       
   214             sb.append(item.toString());
       
   215             currSep = sep;
       
   216         }
       
   217         return sb.toString();
       
   218     }
       
   219 
       
   220     void cleanup() {
       
   221         if (compiler != null)
       
   222             compiler.close();
       
   223         if (fileManager instanceof BaseFileManager && ((BaseFileManager) fileManager).autoClose) {
       
   224             try {
       
   225                 fileManager.close();
       
   226             } catch (IOException ignore) {
       
   227             }
       
   228         }
       
   229         compiler = null;
       
   230         context = null;
       
   231         notYetEntered = null;
       
   232     }
       
   233 
       
   234     @Override @DefinedBy(Api.COMPILER_TREE)
       
   235     public Iterable<? extends CompilationUnitTree> parse() {
       
   236         return handleExceptions(this::parseInternal, List.nil(), List.nil());
       
   237     }
       
   238 
       
   239     private Iterable<? extends CompilationUnitTree> parseInternal() {
       
   240         try {
       
   241             prepareCompiler(true);
       
   242             List<JCCompilationUnit> units = compiler.parseFiles(args.getFileObjects());
       
   243             for (JCCompilationUnit unit: units) {
       
   244                 JavaFileObject file = unit.getSourceFile();
       
   245                 if (notYetEntered.containsKey(file))
       
   246                     notYetEntered.put(file, unit);
       
   247             }
       
   248             return units;
       
   249         }
       
   250         finally {
       
   251             parsed = true;
       
   252             if (compiler != null && compiler.log != null)
       
   253                 compiler.log.flush();
       
   254         }
       
   255     }
       
   256 
       
   257     private boolean parsed = false;
       
   258 
       
   259     /**
       
   260      * Translate all the abstract syntax trees to elements.
       
   261      *
       
   262      * @return a list of elements corresponding to the top level
       
   263      * classes in the abstract syntax trees
       
   264      */
       
   265     public Iterable<? extends Element> enter() {
       
   266         return enter(null);
       
   267     }
       
   268 
       
   269     /**
       
   270      * Translate the given abstract syntax trees to elements.
       
   271      *
       
   272      * @param trees a list of abstract syntax trees.
       
   273      * @return a list of elements corresponding to the top level
       
   274      * classes in the abstract syntax trees
       
   275      */
       
   276     public Iterable<? extends Element> enter(Iterable<? extends CompilationUnitTree> trees)
       
   277     {
       
   278         if (trees == null && notYetEntered != null && notYetEntered.isEmpty())
       
   279             return List.nil();
       
   280 
       
   281         boolean wasInitialized = compiler != null;
       
   282 
       
   283         prepareCompiler(true);
       
   284 
       
   285         ListBuffer<JCCompilationUnit> roots = null;
       
   286 
       
   287         if (trees == null) {
       
   288             // If there are still files which were specified to be compiled
       
   289             // (i.e. in fileObjects) but which have not yet been entered,
       
   290             // then we make sure they have been parsed and add them to the
       
   291             // list to be entered.
       
   292             if (notYetEntered.size() > 0) {
       
   293                 if (!parsed)
       
   294                     parseInternal(); // TODO would be nice to specify files needed to be parsed
       
   295                 for (JavaFileObject file: args.getFileObjects()) {
       
   296                     JCCompilationUnit unit = notYetEntered.remove(file);
       
   297                     if (unit != null) {
       
   298                         if (roots == null)
       
   299                             roots = new ListBuffer<>();
       
   300                         roots.append(unit);
       
   301                     }
       
   302                 }
       
   303                 notYetEntered.clear();
       
   304             }
       
   305         }
       
   306         else {
       
   307             for (CompilationUnitTree cu : trees) {
       
   308                 if (cu instanceof JCCompilationUnit) {
       
   309                     if (roots == null)
       
   310                         roots = new ListBuffer<>();
       
   311                     roots.append((JCCompilationUnit)cu);
       
   312                     notYetEntered.remove(cu.getSourceFile());
       
   313                 }
       
   314                 else
       
   315                     throw new IllegalArgumentException(cu.toString());
       
   316             }
       
   317         }
       
   318 
       
   319         if (roots == null) {
       
   320             if (trees == null && !wasInitialized) {
       
   321                 compiler.initModules(List.nil());
       
   322             }
       
   323             return List.nil();
       
   324         }
       
   325 
       
   326         List<JCCompilationUnit> units = compiler.initModules(roots.toList());
       
   327 
       
   328         try {
       
   329             units = compiler.enterTrees(units);
       
   330 
       
   331             if (notYetEntered.isEmpty())
       
   332                 compiler.processAnnotations(units);
       
   333 
       
   334             ListBuffer<Element> elements = new ListBuffer<>();
       
   335             for (JCCompilationUnit unit : units) {
       
   336                 boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info",
       
   337                                                                      JavaFileObject.Kind.SOURCE);
       
   338                 if (isPkgInfo) {
       
   339                     elements.append(unit.packge);
       
   340                 } else {
       
   341                     for (JCTree node : unit.defs) {
       
   342                         if (node.hasTag(JCTree.Tag.CLASSDEF)) {
       
   343                             JCClassDecl cdef = (JCClassDecl) node;
       
   344                             if (cdef.sym != null) // maybe null if errors in anno processing
       
   345                                 elements.append(cdef.sym);
       
   346                         } else if (node.hasTag(JCTree.Tag.MODULEDEF)) {
       
   347                             JCModuleDecl mdef = (JCModuleDecl) node;
       
   348                             if (mdef.sym != null)
       
   349                                 elements.append(mdef.sym);
       
   350                         }
       
   351                     }
       
   352                 }
       
   353             }
       
   354             return elements.toList();
       
   355         }
       
   356         finally {
       
   357             compiler.log.flush();
       
   358         }
       
   359     }
       
   360 
       
   361     @Override @DefinedBy(Api.COMPILER_TREE)
       
   362     public Iterable<? extends Element> analyze() {
       
   363         return handleExceptions(() -> analyze(null), List.nil(), List.nil());
       
   364     }
       
   365 
       
   366     /**
       
   367      * Complete all analysis on the given classes.
       
   368      * This can be used to ensure that all compile time errors are reported.
       
   369      * The classes must have previously been returned from {@link #enter}.
       
   370      * If null is specified, all outstanding classes will be analyzed.
       
   371      *
       
   372      * @param classes a list of class elements
       
   373      * @return the elements that were analyzed
       
   374      */
       
   375     // This implementation requires that we open up privileges on JavaCompiler.
       
   376     // An alternative implementation would be to move this code to JavaCompiler and
       
   377     // wrap it here
       
   378     public Iterable<? extends Element> analyze(Iterable<? extends Element> classes) {
       
   379         enter(null);  // ensure all classes have been entered
       
   380 
       
   381         final ListBuffer<Element> results = new ListBuffer<>();
       
   382         try {
       
   383             if (classes == null) {
       
   384                 handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results);
       
   385             } else {
       
   386                 Filter f = new Filter() {
       
   387                     @Override
       
   388                     public void process(Env<AttrContext> env) {
       
   389                         handleFlowResults(compiler.flow(compiler.attribute(env)), results);
       
   390                     }
       
   391                 };
       
   392                 f.run(compiler.todo, classes);
       
   393             }
       
   394         } finally {
       
   395             compiler.log.flush();
       
   396         }
       
   397         return results;
       
   398     }
       
   399     // where
       
   400         private void handleFlowResults(Queue<Env<AttrContext>> queue, ListBuffer<Element> elems) {
       
   401             for (Env<AttrContext> env: queue) {
       
   402                 switch (env.tree.getTag()) {
       
   403                     case CLASSDEF:
       
   404                         JCClassDecl cdef = (JCClassDecl) env.tree;
       
   405                         if (cdef.sym != null)
       
   406                             elems.append(cdef.sym);
       
   407                         break;
       
   408                     case MODULEDEF:
       
   409                         JCModuleDecl mod = (JCModuleDecl) env.tree;
       
   410                         if (mod.sym != null)
       
   411                             elems.append(mod.sym);
       
   412                         break;
       
   413                     case PACKAGEDEF:
       
   414                         JCCompilationUnit unit = env.toplevel;
       
   415                         if (unit.packge != null)
       
   416                             elems.append(unit.packge);
       
   417                         break;
       
   418                 }
       
   419             }
       
   420             genList.addAll(queue);
       
   421         }
       
   422 
       
   423     @Override @DefinedBy(Api.COMPILER_TREE)
       
   424     public Iterable<? extends JavaFileObject> generate() {
       
   425         return handleExceptions(() -> generate(null), List.nil(), List.nil());
       
   426     }
       
   427 
       
   428     /**
       
   429      * Generate code corresponding to the given classes.
       
   430      * The classes must have previously been returned from {@link #enter}.
       
   431      * If there are classes outstanding to be analyzed, that will be done before
       
   432      * any classes are generated.
       
   433      * If null is specified, code will be generated for all outstanding classes.
       
   434      *
       
   435      * @param classes a list of class elements
       
   436      * @return the files that were generated
       
   437      */
       
   438     public Iterable<? extends JavaFileObject> generate(Iterable<? extends Element> classes) {
       
   439         final ListBuffer<JavaFileObject> results = new ListBuffer<>();
       
   440         try {
       
   441             analyze(null);  // ensure all classes have been parsed, entered, and analyzed
       
   442 
       
   443             if (classes == null) {
       
   444                 compiler.generate(compiler.desugar(genList), results);
       
   445                 genList.clear();
       
   446             }
       
   447             else {
       
   448                 Filter f = new Filter() {
       
   449                         @Override
       
   450                         public void process(Env<AttrContext> env) {
       
   451                             compiler.generate(compiler.desugar(ListBuffer.of(env)), results);
       
   452                         }
       
   453                     };
       
   454                 f.run(genList, classes);
       
   455             }
       
   456             if (genList.isEmpty()) {
       
   457                 compiler.reportDeferredDiagnostics();
       
   458                 cleanup();
       
   459             }
       
   460         }
       
   461         finally {
       
   462             if (compiler != null)
       
   463                 compiler.log.flush();
       
   464         }
       
   465         return results;
       
   466     }
       
   467 
       
   468     public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) {
       
   469         return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse();
       
   470     }
       
   471 
       
   472     public void ensureEntered() {
       
   473         args.allowEmpty();
       
   474         enter(null);
       
   475     }
       
   476 
       
   477     abstract class Filter {
       
   478         void run(Queue<Env<AttrContext>> list, Iterable<? extends Element> elements) {
       
   479             Set<Element> set = new HashSet<>();
       
   480             for (Element item: elements) {
       
   481                 set.add(item);
       
   482             }
       
   483 
       
   484             ListBuffer<Env<AttrContext>> defer = new ListBuffer<>();
       
   485             while (list.peek() != null) {
       
   486                 Env<AttrContext> env = list.remove();
       
   487                 Symbol test = null;
       
   488 
       
   489                 if (env.tree.hasTag(Tag.MODULEDEF)) {
       
   490                     test = ((JCModuleDecl) env.tree).sym;
       
   491                 } else if (env.tree.hasTag(Tag.PACKAGEDEF)) {
       
   492                     test = env.toplevel.packge;
       
   493                 } else {
       
   494                     ClassSymbol csym = env.enclClass.sym;
       
   495                     if (csym != null)
       
   496                         test = csym.outermostClass();
       
   497                 }
       
   498                 if (test != null && set.contains(test))
       
   499                     process(env);
       
   500                 else
       
   501                     defer = defer.append(env);
       
   502             }
       
   503 
       
   504             list.addAll(defer);
       
   505         }
       
   506 
       
   507         abstract void process(Env<AttrContext> env);
       
   508     }
       
   509 
       
   510     /**
       
   511      * For internal use only.  This method will be
       
   512      * removed without warning.
       
   513      * @param expr the type expression to be analyzed
       
   514      * @param scope the scope in which to analyze the type expression
       
   515      * @return the type
       
   516      * @throws IllegalArgumentException if the type expression of null or empty
       
   517      */
       
   518     public Type parseType(String expr, TypeElement scope) {
       
   519         if (expr == null || expr.equals(""))
       
   520             throw new IllegalArgumentException();
       
   521         compiler = JavaCompiler.instance(context);
       
   522         JavaFileObject prev = compiler.log.useSource(null);
       
   523         ParserFactory parserFactory = ParserFactory.instance(context);
       
   524         Attr attr = Attr.instance(context);
       
   525         try {
       
   526             CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length());
       
   527             Parser parser = parserFactory.newParser(buf, false, false, false);
       
   528             JCTree tree = parser.parseType();
       
   529             return attr.attribType(tree, (Symbol.TypeSymbol)scope);
       
   530         } finally {
       
   531             compiler.log.useSource(prev);
       
   532         }
       
   533     }
       
   534 
       
   535 }