langtools/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java
changeset 10 06bc494ca11e
child 731 1dd22bdb9ca5
--- /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&eacute;
+ * @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);
+        }
+    }
+
+}