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

Tue, 09 Sep 2008 10:40:50 -0700

author
jjg
date
Tue, 09 Sep 2008 10:40:50 -0700
changeset 111
a92b756a888f
parent 70
62fcf8d73dc5
child 119
1e83972f53fb
permissions
-rw-r--r--

6724118: change JavaCompiler to not use Scanner directly
6736119: refactor Parser and Parser.Factory
Reviewed-by: 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.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.model.*;
    48 import com.sun.tools.javac.parser.Parser;
    49 import com.sun.tools.javac.parser.ParserFactory;
    50 import com.sun.tools.javac.tree.*;
    51 import com.sun.tools.javac.tree.JCTree.*;
    52 import com.sun.tools.javac.util.*;
    53 import com.sun.tools.javac.util.List;
    54 import com.sun.tools.javac.main.JavaCompiler;
    56 /**
    57  * Provides access to functionality specific to the Sun Java Compiler, javac.
    58  *
    59  * <p><b>This is NOT part of any API supported by Sun Microsystems.
    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 JavacTask {
    68     private JavacTool tool;
    69     private Main compilerMain;
    70     private JavaCompiler compiler;
    71     private String[] args;
    72     private Context context;
    73     private List<JavaFileObject> fileObjects;
    74     private Map<JavaFileObject, JCCompilationUnit> notYetEntered;
    75     private ListBuffer<Env<AttrContext>> genList;
    76     private TaskListener taskListener;
    77     private AtomicBoolean used = new AtomicBoolean();
    78     private Iterable<? extends Processor> processors;
    80     private Integer result = null;
    82     JavacTaskImpl(JavacTool tool,
    83                 Main compilerMain,
    84                 String[] args,
    85                 Context context,
    86                 List<JavaFileObject> fileObjects) {
    87         this.tool = tool;
    88         this.compilerMain = compilerMain;
    89         this.args = args;
    90         this.context = context;
    91         this.fileObjects = fileObjects;
    92         // null checks
    93         compilerMain.getClass();
    94         args.getClass();
    95         context.getClass();
    96         fileObjects.getClass();
    98         // force the use of the scanner that captures Javadoc comments
    99         com.sun.tools.javac.parser.DocCommentScanner.Factory.preRegister(context);
   100     }
   102     JavacTaskImpl(JavacTool tool,
   103                 Main compilerMain,
   104                 Iterable<String> flags,
   105                 Context context,
   106                 Iterable<String> classes,
   107                 Iterable<? extends JavaFileObject> fileObjects) {
   108         this(tool, compilerMain, toArray(flags, classes), context, toList(fileObjects));
   109     }
   111     static private String[] toArray(Iterable<String> flags, Iterable<String> classes) {
   112         ListBuffer<String> result = new ListBuffer<String>();
   113         if (flags != null)
   114             for (String flag : flags)
   115                 result.append(flag);
   116         if (classes != null)
   117             for (String cls : classes)
   118                 result.append(cls);
   119         return result.toArray(new String[result.length()]);
   120     }
   122     static private List<JavaFileObject> toList(Iterable<? extends JavaFileObject> fileObjects) {
   123         if (fileObjects == null)
   124             return List.nil();
   125         ListBuffer<JavaFileObject> result = new ListBuffer<JavaFileObject>();
   126         for (JavaFileObject fo : fileObjects)
   127             result.append(fo);
   128         return result.toList();
   129     }
   131     public Boolean call() {
   132         if (!used.getAndSet(true)) {
   133             beginContext();
   134             try {
   135                 compilerMain.setFatalErrors(true);
   136                 result = compilerMain.compile(args, context, fileObjects, processors);
   137             } finally {
   138                 endContext();
   139             }
   140             compilerMain = null;
   141             args = null;
   142             context = null;
   143             fileObjects = null;
   144             return result == 0;
   145         } else {
   146             throw new IllegalStateException("multiple calls to method 'call'");
   147         }
   148     }
   150     public void setProcessors(Iterable<? extends Processor> processors) {
   151         processors.getClass(); // null check
   152         // not mt-safe
   153         if (used.get())
   154             throw new IllegalStateException();
   155         this.processors = processors;
   156     }
   158     public void setLocale(Locale locale) {
   159         // locale argument is ignored, see RFE 6443132
   160         if (used.get())
   161             throw new IllegalStateException();
   162     }
   164     private void prepareCompiler() throws IOException {
   165         if (!used.getAndSet(true)) {
   166             beginContext();
   167             compilerMain.setOptions(Options.instance(context));
   168             compilerMain.filenames = new ListBuffer<File>();
   169             List<File> filenames = compilerMain.processArgs(CommandLine.parse(args));
   170             if (!filenames.isEmpty())
   171                 throw new IllegalArgumentException("Malformed arguments " + filenames.toString(" "));
   172             compiler = JavaCompiler.instance(context);
   173             compiler.keepComments = true;
   174             compiler.genEndPos = true;
   175             // NOTE: this value will be updated after annotation processing
   176             compiler.initProcessAnnotations(processors);
   177             notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>();
   178             for (JavaFileObject file: fileObjects)
   179                 notYetEntered.put(file, null);
   180             genList = new ListBuffer<Env<AttrContext>>();
   181             // endContext will be called when all classes have been generated
   182             // TODO: should handle the case after each phase if errors have occurred
   183             args = null;
   184         }
   185     }
   187     private void beginContext() {
   188         context.put(JavacTaskImpl.class, this);
   189         if (context.get(TaskListener.class) != null)
   190             context.put(TaskListener.class, (TaskListener)null);
   191         if (taskListener != null)
   192             context.put(TaskListener.class, wrap(taskListener));
   193         tool.beginContext(context);
   194     }
   195     // where
   196     private TaskListener wrap(final TaskListener tl) {
   197         tl.getClass(); // null check
   198         return new TaskListener() {
   199             public void started(TaskEvent e) {
   200                 try {
   201                     tl.started(e);
   202                 } catch (Throwable t) {
   203                     throw new ClientCodeException(t);
   204                 }
   205             }
   207             public void finished(TaskEvent e) {
   208                 try {
   209                     tl.finished(e);
   210                 } catch (Throwable t) {
   211                     throw new ClientCodeException(t);
   212                 }
   213             }
   215         };
   216     }
   218     private void endContext() {
   219         tool.endContext();
   220     }
   222     /**
   223      * Construct a JavaFileObject from the given file.
   224      *
   225      * <p><b>TODO: this method is useless here</b></p>
   226      *
   227      * @param file a file
   228      * @return a JavaFileObject from the standard file manager.
   229      */
   230     public JavaFileObject asJavaFileObject(File file) {
   231         JavacFileManager fm = (JavacFileManager)context.get(JavaFileManager.class);
   232         return fm.getRegularFile(file);
   233     }
   235     public void setTaskListener(TaskListener taskListener) {
   236         this.taskListener = taskListener;
   237     }
   239     /**
   240      * Parse the specified files returning a list of abstract syntax trees.
   241      *
   242      * @throws java.io.IOException TODO
   243      * @return a list of abstract syntax trees
   244      */
   245     public Iterable<? extends CompilationUnitTree> parse() throws IOException {
   246         try {
   247             prepareCompiler();
   248             List<JCCompilationUnit> units = compiler.parseFiles(fileObjects);
   249             for (JCCompilationUnit unit: units) {
   250                 JavaFileObject file = unit.getSourceFile();
   251                 if (notYetEntered.containsKey(file))
   252                     notYetEntered.put(file, unit);
   253             }
   254             return units;
   255         }
   256         finally {
   257             parsed = true;
   258             if (compiler != null && compiler.log != null)
   259                 compiler.log.flush();
   260         }
   261     }
   263     private boolean parsed = false;
   265     /**
   266      * Translate all the abstract syntax trees to elements.
   267      *
   268      * @throws IOException TODO
   269      * @return a list of elements corresponding to the top level
   270      * classes in the abstract syntax trees
   271      */
   272     public Iterable<? extends TypeElement> enter() throws IOException {
   273         return enter(null);
   274     }
   276     /**
   277      * Translate the given abstract syntax trees to elements.
   278      *
   279      * @param trees a list of abstract syntax trees.
   280      * @throws java.io.IOException TODO
   281      * @return a list of elements corresponding to the top level
   282      * classes in the abstract syntax trees
   283      */
   284     public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees)
   285         throws IOException
   286     {
   287         prepareCompiler();
   289         ListBuffer<JCCompilationUnit> roots = null;
   291         if (trees == null) {
   292             // If there are still files which were specified to be compiled
   293             // (i.e. in fileObjects) but which have not yet been entered,
   294             // then we make sure they have been parsed and add them to the
   295             // list to be entered.
   296             if (notYetEntered.size() > 0) {
   297                 if (!parsed)
   298                     parse(); // TODO would be nice to specify files needed to be parsed
   299                 for (JavaFileObject file: fileObjects) {
   300                     JCCompilationUnit unit = notYetEntered.remove(file);
   301                     if (unit != null) {
   302                         if (roots == null)
   303                             roots = new ListBuffer<JCCompilationUnit>();
   304                         roots.append(unit);
   305                     }
   306                 }
   307                 notYetEntered.clear();
   308             }
   309         }
   310         else {
   311             for (CompilationUnitTree cu : trees) {
   312                 if (cu instanceof JCCompilationUnit) {
   313                     if (roots == null)
   314                         roots = new ListBuffer<JCCompilationUnit>();
   315                     roots.append((JCCompilationUnit)cu);
   316                     notYetEntered.remove(cu.getSourceFile());
   317                 }
   318                 else
   319                     throw new IllegalArgumentException(cu.toString());
   320             }
   321         }
   323         if (roots == null)
   324             return List.nil();
   326         try {
   327             List<JCCompilationUnit> units = compiler.enterTrees(roots.toList());
   329             if (notYetEntered.isEmpty())
   330                 compiler = compiler.processAnnotations(units);
   332             ListBuffer<TypeElement> elements = new ListBuffer<TypeElement>();
   333             for (JCCompilationUnit unit : units) {
   334                 for (JCTree node : unit.defs)
   335                     if (node.getTag() == JCTree.CLASSDEF)
   336                         elements.append(((JCTree.JCClassDecl) node).sym);
   337             }
   338             return elements.toList();
   339         }
   340         finally {
   341             compiler.log.flush();
   342         }
   343     }
   345     /**
   346      * Complete all analysis.
   347      * @throws IOException TODO
   348      */
   349     @Override
   350     public Iterable<? extends Element> analyze() throws IOException {
   351         return analyze(null);
   352     }
   354     /**
   355      * Complete all analysis on the given classes.
   356      * This can be used to ensure that all compile time errors are reported.
   357      * The classes must have previously been returned from {@link #enter}.
   358      * If null is specified, all outstanding classes will be analyzed.
   359      *
   360      * @param classes a list of class elements
   361      */
   362     // This implementation requires that we open up privileges on JavaCompiler.
   363     // An alternative implementation would be to move this code to JavaCompiler and
   364     // wrap it here
   365     public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) throws IOException {
   366         enter(null);  // ensure all classes have been entered
   368         final ListBuffer<Element> results = new ListBuffer<Element>();
   369         try {
   370             if (classes == null) {
   371                 handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results);
   372             } else {
   373                 Filter f = new Filter() {
   374                     public void process(Env<AttrContext> env) {
   375                         handleFlowResults(compiler.flow(compiler.attribute(env)), results);
   376                     }
   377                 };
   378                 f.run(compiler.todo, classes);
   379             }
   380         } finally {
   381             compiler.log.flush();
   382         }
   383         return results;
   384     }
   385     // where
   386         private void handleFlowResults(Queue<Env<AttrContext>> queue, ListBuffer<Element> elems) {
   387             for (Env<AttrContext> env: queue) {
   388                 switch (env.tree.getTag()) {
   389                     case JCTree.CLASSDEF:
   390                         JCClassDecl cdef = (JCClassDecl) env.tree;
   391                         if (cdef.sym != null)
   392                             elems.append(cdef.sym);
   393                         break;
   394                     case JCTree.TOPLEVEL:
   395                         JCCompilationUnit unit = (JCCompilationUnit) env.tree;
   396                         if (unit.packge != null)
   397                             elems.append(unit.packge);
   398                         break;
   399                 }
   400             }
   401             genList.addAll(queue);
   402         }
   405     /**
   406      * Generate code.
   407      * @throws IOException TODO
   408      */
   409     @Override
   410     public Iterable<? extends JavaFileObject> generate() throws IOException {
   411         return generate(null);
   412     }
   414     /**
   415      * Generate code corresponding to the given classes.
   416      * The classes must have previously been returned from {@link #enter}.
   417      * If there are classes outstanding to be analyzed, that will be done before
   418      * any classes are generated.
   419      * If null is specified, code will be generated for all outstanding classes.
   420      *
   421      * @param classes a list of class elements
   422      */
   423     public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) throws IOException {
   424         final ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
   425         try {
   426             analyze(null);  // ensure all classes have been parsed, entered, and analyzed
   428             if (classes == null) {
   429                 compiler.generate(compiler.desugar(genList), results);
   430                 genList.clear();
   431             }
   432             else {
   433                 Filter f = new Filter() {
   434                         public void process(Env<AttrContext> env) {
   435                             compiler.generate(compiler.desugar(ListBuffer.of(env)), results);
   436                         }
   437                     };
   438                 f.run(genList, classes);
   439             }
   440             if (genList.isEmpty()) {
   441                 compiler.reportDeferredDiagnostics();
   442                 compiler.log.flush();
   443                 endContext();
   444             }
   445         }
   446         finally {
   447             compiler.log.flush();
   448         }
   449         return results;
   450     }
   452     public TypeMirror getTypeMirror(Iterable<? extends Tree> path) {
   453         // TODO: Should complete attribution if necessary
   454         Tree last = null;
   455         for (Tree node : path)
   456             last = node;
   457         return ((JCTree)last).type;
   458     }
   460     public JavacElements getElements() {
   461         if (context == null)
   462             throw new IllegalStateException();
   463         return JavacElements.instance(context);
   464     }
   466     public JavacTypes getTypes() {
   467         if (context == null)
   468             throw new IllegalStateException();
   469         return JavacTypes.instance(context);
   470     }
   472     public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) {
   473         return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse();
   474     }
   476     abstract class Filter {
   477         void run(ListBuffer<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) {
   478             Set<TypeElement> set = new HashSet<TypeElement>();
   479             for (TypeElement item: classes)
   480                 set.add(item);
   482             List<Env<AttrContext>> defer = List.<Env<AttrContext>>nil();
   483             while (list.nonEmpty()) {
   484                 Env<AttrContext> env = list.next();
   485                 ClassSymbol csym = env.enclClass.sym;
   486                 if (csym != null && set.contains(csym.outermostClass()))
   487                     process(env);
   488                 else
   489                     defer = defer.prepend(env);
   490             }
   492             for (List<Env<AttrContext>> l = defer; l.nonEmpty(); l = l.tail)
   493                 list.prepend(l.head);
   494         }
   496         abstract void process(Env<AttrContext> env);
   497     }
   499     /**
   500      * For internal use by Sun Microsystems only.  This method will be
   501      * removed without warning.
   502      */
   503     public Context getContext() {
   504         return context;
   505     }
   507     /**
   508      * For internal use by Sun Microsystems only.  This method will be
   509      * removed without warning.
   510      */
   511     public void updateContext(Context newContext) {
   512         context = newContext;
   513     }
   515     /**
   516      * For internal use by Sun Microsystems only.  This method will be
   517      * removed without warning.
   518      */
   519     public Type parseType(String expr, TypeElement scope) {
   520         if (expr == null || expr.equals(""))
   521             throw new IllegalArgumentException();
   522         compiler = JavaCompiler.instance(context);
   523         JavaFileObject prev = compiler.log.useSource(null);
   524         ParserFactory parserFactory = ParserFactory.instance(context);
   525         Attr attr = Attr.instance(context);
   526         try {
   527             CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length());
   528             Parser parser = parserFactory.newParser(buf, false, false, false);
   529             JCTree tree = parser.parseType();
   530             return attr.attribType(tree, (Symbol.TypeSymbol)scope);
   531         } finally {
   532             compiler.log.useSource(prev);
   533         }
   534     }
   536 }

mercurial