--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,536 @@
+/*
+ * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.javac.api;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.processing.Processor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.tools.*;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.*;
+import com.sun.source.util.*;
+import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Symbol.*;
+import com.sun.tools.javac.comp.*;
+import com.sun.tools.javac.main.*;
+import com.sun.tools.javac.model.*;
+import com.sun.tools.javac.parser.Parser;
+import com.sun.tools.javac.parser.Scanner;
+import com.sun.tools.javac.tree.*;
+import com.sun.tools.javac.tree.JCTree.*;
+import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.main.JavaCompiler;
+
+/**
+ * Provides access to functionality specific to the Sun Java Compiler, javac.
+ *
+ * <p><b>This is NOT part of any API supported by Sun Microsystems.
+ * If you write code that depends on this, you do so at your own
+ * risk. This code and its internal interfaces are subject to change
+ * or deletion without notice.</b></p>
+ *
+ * @author Peter von der Ahé
+ * @author Jonathan Gibbons
+ */
+public class JavacTaskImpl extends JavacTask {
+ private JavacTool tool;
+ private Main compilerMain;
+ private JavaCompiler compiler;
+ private String[] args;
+ private Context context;
+ private List<JavaFileObject> fileObjects;
+ private Map<JavaFileObject, JCCompilationUnit> notYetEntered;
+ private ListBuffer<Env<AttrContext>> genList;
+ private TaskListener taskListener;
+ private AtomicBoolean used = new AtomicBoolean();
+ private Iterable<? extends Processor> processors;
+
+ private Integer result = null;
+
+ JavacTaskImpl(JavacTool tool,
+ Main compilerMain,
+ String[] args,
+ Context context,
+ List<JavaFileObject> fileObjects) {
+ this.tool = tool;
+ this.compilerMain = compilerMain;
+ this.args = args;
+ this.context = context;
+ this.fileObjects = fileObjects;
+ // null checks
+ compilerMain.getClass();
+ args.getClass();
+ context.getClass();
+ fileObjects.getClass();
+ }
+
+ JavacTaskImpl(JavacTool tool,
+ Main compilerMain,
+ Iterable<String> flags,
+ Context context,
+ Iterable<String> classes,
+ Iterable<? extends JavaFileObject> fileObjects) {
+ this(tool, compilerMain, toArray(flags, classes), context, toList(fileObjects));
+ }
+
+ static private String[] toArray(Iterable<String> flags, Iterable<String> classes) {
+ ListBuffer<String> result = new ListBuffer<String>();
+ if (flags != null)
+ for (String flag : flags)
+ result.append(flag);
+ if (classes != null)
+ for (String cls : classes)
+ result.append(cls);
+ return result.toArray(new String[result.length()]);
+ }
+
+ static private List<JavaFileObject> toList(Iterable<? extends JavaFileObject> fileObjects) {
+ if (fileObjects == null)
+ return List.nil();
+ ListBuffer<JavaFileObject> result = new ListBuffer<JavaFileObject>();
+ for (JavaFileObject fo : fileObjects)
+ result.append(fo);
+ return result.toList();
+ }
+
+ public Boolean call() {
+ if (!used.getAndSet(true)) {
+ beginContext();
+ try {
+ compilerMain.setFatalErrors(true);
+ result = compilerMain.compile(args, context, fileObjects, processors);
+ } finally {
+ endContext();
+ }
+ compilerMain = null;
+ args = null;
+ context = null;
+ fileObjects = null;
+ return result == 0;
+ } else {
+ throw new IllegalStateException("multiple calls to method 'call'");
+ }
+ }
+
+ public void setProcessors(Iterable<? extends Processor> processors) {
+ processors.getClass(); // null check
+ // not mt-safe
+ if (used.get())
+ throw new IllegalStateException();
+ this.processors = processors;
+ }
+
+ public void setLocale(Locale locale) {
+ // locale argument is ignored, see RFE 6443132
+ if (used.get())
+ throw new IllegalStateException();
+ }
+
+ private void prepareCompiler() throws IOException {
+ if (!used.getAndSet(true)) {
+ beginContext();
+ compilerMain.setOptions(Options.instance(context));
+ compilerMain.filenames = new ListBuffer<File>();
+ List<File> filenames = compilerMain.processArgs(CommandLine.parse(args));
+ if (!filenames.isEmpty())
+ throw new IllegalArgumentException("Malformed arguments " + filenames.toString(" "));
+ compiler = JavaCompiler.instance(context);
+ // force the use of the scanner that captures Javadoc comments
+ com.sun.tools.javac.parser.DocCommentScanner.Factory.preRegister(context);
+ compiler.keepComments = true;
+ compiler.genEndPos = true;
+ // NOTE: this value will be updated after annotation processing
+ compiler.initProcessAnnotations(processors);
+ notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>();
+ for (JavaFileObject file: fileObjects)
+ notYetEntered.put(file, null);
+ genList = new ListBuffer<Env<AttrContext>>();
+ // endContext will be called when all classes have been generated
+ // TODO: should handle the case after each phase if errors have occurred
+ args = null;
+ }
+ }
+
+ private void beginContext() {
+ context.put(JavacTaskImpl.class, this);
+ if (context.get(TaskListener.class) != null)
+ context.put(TaskListener.class, (TaskListener)null);
+ if (taskListener != null)
+ context.put(TaskListener.class, wrap(taskListener));
+ tool.beginContext(context);
+ }
+ // where
+ private TaskListener wrap(final TaskListener tl) {
+ tl.getClass(); // null check
+ return new TaskListener() {
+ public void started(TaskEvent e) {
+ try {
+ tl.started(e);
+ } catch (Throwable t) {
+ throw new ClientCodeException(t);
+ }
+ }
+
+ public void finished(TaskEvent e) {
+ try {
+ tl.finished(e);
+ } catch (Throwable t) {
+ throw new ClientCodeException(t);
+ }
+ }
+
+ };
+ }
+
+ private void endContext() {
+ tool.endContext();
+ }
+
+ /**
+ * Construct a JavaFileObject from the given file.
+ *
+ * <p><b>TODO: this method is useless here</b></p>
+ *
+ * @param file a file
+ * @return a JavaFileObject from the standard file manager.
+ */
+ public JavaFileObject asJavaFileObject(File file) {
+ JavacFileManager fm = (JavacFileManager)context.get(JavaFileManager.class);
+ return fm.getRegularFile(file);
+ }
+
+ public void setTaskListener(TaskListener taskListener) {
+ this.taskListener = taskListener;
+ }
+
+ /**
+ * Parse the specified files returning a list of abstract syntax trees.
+ *
+ * @throws java.io.IOException TODO
+ * @return a list of abstract syntax trees
+ */
+ public Iterable<? extends CompilationUnitTree> parse() throws IOException {
+ try {
+ prepareCompiler();
+ List<JCCompilationUnit> units = compiler.parseFiles(fileObjects);
+ for (JCCompilationUnit unit: units) {
+ JavaFileObject file = unit.getSourceFile();
+ if (notYetEntered.containsKey(file))
+ notYetEntered.put(file, unit);
+ }
+ return units;
+ }
+ finally {
+ parsed = true;
+ if (compiler != null && compiler.log != null)
+ compiler.log.flush();
+ }
+ }
+
+ private boolean parsed = false;
+
+ /**
+ * Translate all the abstract syntax trees to elements.
+ *
+ * @throws IOException TODO
+ * @return a list of elements corresponding to the top level
+ * classes in the abstract syntax trees
+ */
+ public Iterable<? extends TypeElement> enter() throws IOException {
+ return enter(null);
+ }
+
+ /**
+ * Translate the given abstract syntax trees to elements.
+ *
+ * @param trees a list of abstract syntax trees.
+ * @throws java.io.IOException TODO
+ * @return a list of elements corresponding to the top level
+ * classes in the abstract syntax trees
+ */
+ public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees)
+ throws IOException
+ {
+ prepareCompiler();
+
+ ListBuffer<JCCompilationUnit> roots = null;
+
+ if (trees == null) {
+ // If there are still files which were specified to be compiled
+ // (i.e. in fileObjects) but which have not yet been entered,
+ // then we make sure they have been parsed and add them to the
+ // list to be entered.
+ if (notYetEntered.size() > 0) {
+ if (!parsed)
+ parse(); // TODO would be nice to specify files needed to be parsed
+ for (JavaFileObject file: fileObjects) {
+ JCCompilationUnit unit = notYetEntered.remove(file);
+ if (unit != null) {
+ if (roots == null)
+ roots = new ListBuffer<JCCompilationUnit>();
+ roots.append(unit);
+ }
+ }
+ notYetEntered.clear();
+ }
+ }
+ else {
+ for (CompilationUnitTree cu : trees) {
+ if (cu instanceof JCCompilationUnit) {
+ if (roots == null)
+ roots = new ListBuffer<JCCompilationUnit>();
+ roots.append((JCCompilationUnit)cu);
+ notYetEntered.remove(cu.getSourceFile());
+ }
+ else
+ throw new IllegalArgumentException(cu.toString());
+ }
+ }
+
+ if (roots == null)
+ return List.nil();
+
+ try {
+ List<JCCompilationUnit> units = compiler.enterTrees(roots.toList());
+
+ if (notYetEntered.isEmpty())
+ compiler = compiler.processAnnotations(units);
+
+ ListBuffer<TypeElement> elements = new ListBuffer<TypeElement>();
+ for (JCCompilationUnit unit : units) {
+ for (JCTree node : unit.defs)
+ if (node.getTag() == JCTree.CLASSDEF)
+ elements.append(((JCTree.JCClassDecl) node).sym);
+ }
+ return elements.toList();
+ }
+ finally {
+ compiler.log.flush();
+ }
+ }
+
+ /**
+ * Complete all analysis.
+ * @throws IOException TODO
+ */
+ @Override
+ public Iterable<? extends Element> analyze() throws IOException {
+ return analyze(null);
+ }
+
+ /**
+ * Complete all analysis on the given classes.
+ * This can be used to ensure that all compile time errors are reported.
+ * The classes must have previously been returned from {@link #enter}.
+ * If null is specified, all outstanding classes will be analyzed.
+ *
+ * @param classes a list of class elements
+ */
+ // This implementation requires that we open up privileges on JavaCompiler.
+ // An alternative implementation would be to move this code to JavaCompiler and
+ // wrap it here
+ public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) throws IOException {
+ enter(null); // ensure all classes have been entered
+
+ final ListBuffer<Element> results = new ListBuffer<Element>();
+ try {
+ if (classes == null) {
+ handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results);
+ } else {
+ Filter f = new Filter() {
+ public void process(Env<AttrContext> env) {
+ handleFlowResults(compiler.flow(compiler.attribute(env)), results);
+ }
+ };
+ f.run(compiler.todo, classes);
+ }
+ } finally {
+ compiler.log.flush();
+ }
+ return results;
+ }
+ // where
+ private void handleFlowResults(List<Env<AttrContext>> list, ListBuffer<Element> elems) {
+ for (Env<AttrContext> env: list) {
+ switch (env.tree.getTag()) {
+ case JCTree.CLASSDEF:
+ JCClassDecl cdef = (JCClassDecl) env.tree;
+ if (cdef.sym != null)
+ elems.append(cdef.sym);
+ break;
+ case JCTree.TOPLEVEL:
+ JCCompilationUnit unit = (JCCompilationUnit) env.tree;
+ if (unit.packge != null)
+ elems.append(unit.packge);
+ break;
+ }
+ }
+ genList.appendList(list);
+ }
+
+
+ /**
+ * Generate code.
+ * @throws IOException TODO
+ */
+ @Override
+ public Iterable<? extends JavaFileObject> generate() throws IOException {
+ return generate(null);
+ }
+
+ /**
+ * Generate code corresponding to the given classes.
+ * The classes must have previously been returned from {@link #enter}.
+ * If there are classes outstanding to be analyzed, that will be done before
+ * any classes are generated.
+ * If null is specified, code will be generated for all outstanding classes.
+ *
+ * @param classes a list of class elements
+ */
+ public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) throws IOException {
+ final ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
+ try {
+ analyze(null); // ensure all classes have been parsed, entered, and analyzed
+
+ if (classes == null) {
+ compiler.generate(compiler.desugar(genList.toList()), results);
+ genList.clear();
+ }
+ else {
+ Filter f = new Filter() {
+ public void process(Env<AttrContext> env) {
+ compiler.generate(compiler.desugar(List.of(env)), results);
+ }
+ };
+ f.run(genList, classes);
+ }
+ if (genList.isEmpty()) {
+ compiler.reportDeferredDiagnostics();
+ compiler.log.flush();
+ endContext();
+ }
+ }
+ finally {
+ compiler.log.flush();
+ }
+ return results;
+ }
+
+ public TypeMirror getTypeMirror(Iterable<? extends Tree> path) {
+ // TODO: Should complete attribution if necessary
+ Tree last = null;
+ for (Tree node : path)
+ last = node;
+ return ((JCTree)last).type;
+ }
+
+ public JavacElements getElements() {
+ if (context == null)
+ throw new IllegalStateException();
+ return JavacElements.instance(context);
+ }
+
+ public JavacTypes getTypes() {
+ if (context == null)
+ throw new IllegalStateException();
+ return JavacTypes.instance(context);
+ }
+
+ public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) {
+ return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse();
+ }
+
+ abstract class Filter {
+ void run(ListBuffer<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) {
+ Set<TypeElement> set = new HashSet<TypeElement>();
+ for (TypeElement item: classes)
+ set.add(item);
+
+ List<Env<AttrContext>> defer = List.<Env<AttrContext>>nil();
+ while (list.nonEmpty()) {
+ Env<AttrContext> env = list.next();
+ ClassSymbol csym = env.enclClass.sym;
+ if (csym != null && set.contains(csym.outermostClass()))
+ process(env);
+ else
+ defer = defer.prepend(env);
+ }
+
+ for (List<Env<AttrContext>> l = defer; l.nonEmpty(); l = l.tail)
+ list.prepend(l.head);
+ }
+
+ abstract void process(Env<AttrContext> env);
+ }
+
+ /**
+ * For internal use by Sun Microsystems only. This method will be
+ * removed without warning.
+ */
+ public Context getContext() {
+ return context;
+ }
+
+ /**
+ * For internal use by Sun Microsystems only. This method will be
+ * removed without warning.
+ */
+ public void updateContext(Context newContext) {
+ context = newContext;
+ }
+
+ /**
+ * For internal use by Sun Microsystems only. This method will be
+ * removed without warning.
+ */
+ public Type parseType(String expr, TypeElement scope) {
+ if (expr == null || expr.equals(""))
+ throw new IllegalArgumentException();
+ compiler = JavaCompiler.instance(context);
+ JavaFileObject prev = compiler.log.useSource(null);
+ Scanner.Factory scannerFactory = Scanner.Factory.instance(context);
+ Parser.Factory parserFactory = Parser.Factory.instance(context);
+ Attr attr = Attr.instance(context);
+ try {
+ Scanner scanner = scannerFactory.newScanner((expr+"\u0000").toCharArray(),
+ expr.length());
+ Parser parser = parserFactory.newParser(scanner, false, false);
+ JCTree tree = parser.type();
+ return attr.attribType(tree, (Symbol.TypeSymbol)scope);
+ } finally {
+ compiler.log.useSource(prev);
+ }
+ }
+
+}