diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2005, 2017, Oracle and/or its affiliates. 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javac.api;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.util.*;
+import java.util.concurrent.Callable;
+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.tools.*;
+
+import com.sun.source.tree.*;
+import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.comp.*;
+import com.sun.tools.javac.file.BaseFileManager;
+import com.sun.tools.javac.main.*;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.parser.Parser;
+import com.sun.tools.javac.parser.ParserFactory;
+import com.sun.tools.javac.processing.AnnotationProcessingError;
+import com.sun.tools.javac.tree.*;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
+import com.sun.tools.javac.tree.JCTree.Tag;
+import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.DefinedBy.Api;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Log.PrefixKind;
+import com.sun.tools.javac.util.Log.WriterKind;
+
+/**
+ * Provides access to functionality specific to the JDK Java Compiler, javac.
+ *
+ *
This is NOT part of any supported API.
+ * 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.
+ *
+ * @author Peter von der Ahé
+ * @author Jonathan Gibbons
+ */
+public class JavacTaskImpl extends BasicJavacTask {
+ private final Arguments args;
+ private JavaCompiler compiler;
+ private JavaFileManager fileManager;
+ private Locale locale;
+ private Map notYetEntered;
+ private ListBuffer> genList;
+ private final AtomicBoolean used = new AtomicBoolean();
+ private Iterable extends Processor> processors;
+ private ListBuffer addModules = new ListBuffer<>();
+
+ protected JavacTaskImpl(Context context) {
+ super(context, true);
+ args = Arguments.instance(context);
+ fileManager = context.get(JavaFileManager.class);
+ }
+
+ @Override @DefinedBy(Api.COMPILER)
+ public Boolean call() {
+ return doCall().isOK();
+ }
+
+ /* Internal version of call exposing Main.Result. */
+ public Main.Result doCall() {
+ try {
+ return handleExceptions(() -> {
+ prepareCompiler(false);
+ if (compiler.errorCount() > 0)
+ return Main.Result.ERROR;
+ compiler.compile(args.getFileObjects(), args.getClassNames(), processors, addModules);
+ return (compiler.errorCount() > 0) ? Main.Result.ERROR : Main.Result.OK; // FIXME?
+ }, Main.Result.SYSERR, Main.Result.ABNORMAL);
+ } finally {
+ try {
+ cleanup();
+ } catch (ClientCodeException e) {
+ throw new RuntimeException(e.getCause());
+ }
+ }
+ }
+
+ @Override @DefinedBy(Api.COMPILER)
+ public void addModules(Iterable moduleNames) {
+ Objects.requireNonNull(moduleNames);
+ // not mt-safe
+ if (used.get())
+ throw new IllegalStateException();
+ for (String m : moduleNames) {
+ Objects.requireNonNull(m);
+ addModules.add(m);
+ }
+ }
+
+ @Override @DefinedBy(Api.COMPILER)
+ public void setProcessors(Iterable extends Processor> processors) {
+ Objects.requireNonNull(processors);
+ // not mt-safe
+ if (used.get())
+ throw new IllegalStateException();
+ this.processors = processors;
+ }
+
+ @Override @DefinedBy(Api.COMPILER)
+ public void setLocale(Locale locale) {
+ if (used.get())
+ throw new IllegalStateException();
+ this.locale = locale;
+ }
+
+ private T handleExceptions(Callable c, T sysErrorResult, T abnormalErrorResult) {
+ try {
+ return c.call();
+ } catch (FatalError ex) {
+ Log log = Log.instance(context);
+ Options options = Options.instance(context);
+ log.printRawLines(ex.getMessage());
+ if (ex.getCause() != null && options.isSet("dev")) {
+ ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE));
+ }
+ return sysErrorResult;
+ } catch (AnnotationProcessingError | ClientCodeException e) {
+ // AnnotationProcessingError is thrown from JavacProcessingEnvironment,
+ // to forward errors thrown from an annotation processor
+ // ClientCodeException is thrown from ClientCodeWrapper,
+ // to forward errors thrown from user-supplied code for Compiler API
+ // as specified by javax.tools.JavaCompiler#getTask
+ // and javax.tools.JavaCompiler.CompilationTask#call
+ throw new RuntimeException(e.getCause());
+ } catch (PropagatedException e) {
+ throw e.getCause();
+ } catch (IllegalStateException e) {
+ throw e;
+ } catch (Exception | Error ex) {
+ // Nasty. If we've already reported an error, compensate
+ // for buggy compiler error recovery by swallowing thrown
+ // exceptions.
+ if (compiler == null || compiler.errorCount() == 0
+ || Options.instance(context).isSet("dev")) {
+ Log log = Log.instance(context);
+ log.printLines(PrefixKind.JAVAC, "msg.bug", JavaCompiler.version());
+ ex.printStackTrace(log.getWriter(WriterKind.NOTICE));
+ }
+ return abnormalErrorResult;
+ }
+ }
+
+ private void prepareCompiler(boolean forParse) {
+ if (used.getAndSet(true)) {
+ if (compiler == null)
+ throw new PropagatedException(new IllegalStateException());
+ } else {
+ args.validate();
+
+ //initialize compiler's default locale
+ context.put(Locale.class, locale);
+
+ // hack
+ JavacMessages messages = context.get(JavacMessages.messagesKey);
+ if (messages != null && !messages.getCurrentLocale().equals(locale))
+ messages.setCurrentLocale(locale);
+
+ initPlugins(args.getPluginOpts());
+ initDocLint(args.getDocLintOpts());
+
+ // init JavaCompiler and queues
+ compiler = JavaCompiler.instance(context);
+ compiler.keepComments = true;
+ compiler.genEndPos = true;
+ notYetEntered = new HashMap<>();
+ if (forParse) {
+ compiler.initProcessAnnotations(processors, args.getFileObjects(), args.getClassNames());
+ for (JavaFileObject file: args.getFileObjects())
+ notYetEntered.put(file, null);
+ genList = new ListBuffer<>();
+ }
+ }
+ }
+
+ String toString(Iterable items, String sep) {
+ String currSep = "";
+ StringBuilder sb = new StringBuilder();
+ for (T item: items) {
+ sb.append(currSep);
+ sb.append(item.toString());
+ currSep = sep;
+ }
+ return sb.toString();
+ }
+
+ void cleanup() {
+ if (compiler != null)
+ compiler.close();
+ if (fileManager instanceof BaseFileManager && ((BaseFileManager) fileManager).autoClose) {
+ try {
+ fileManager.close();
+ } catch (IOException ignore) {
+ }
+ }
+ compiler = null;
+ context = null;
+ notYetEntered = null;
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Iterable extends CompilationUnitTree> parse() {
+ return handleExceptions(this::parseInternal, List.nil(), List.nil());
+ }
+
+ private Iterable extends CompilationUnitTree> parseInternal() {
+ try {
+ prepareCompiler(true);
+ List units = compiler.parseFiles(args.getFileObjects());
+ 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.
+ *
+ * @return a list of elements corresponding to the top level
+ * classes in the abstract syntax trees
+ */
+ public Iterable extends Element> enter() {
+ return enter(null);
+ }
+
+ /**
+ * Translate the given abstract syntax trees to elements.
+ *
+ * @param trees a list of abstract syntax trees.
+ * @return a list of elements corresponding to the top level
+ * classes in the abstract syntax trees
+ */
+ public Iterable extends Element> enter(Iterable extends CompilationUnitTree> trees)
+ {
+ if (trees == null && notYetEntered != null && notYetEntered.isEmpty())
+ return List.nil();
+
+ boolean wasInitialized = compiler != null;
+
+ prepareCompiler(true);
+
+ ListBuffer 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)
+ parseInternal(); // TODO would be nice to specify files needed to be parsed
+ for (JavaFileObject file: args.getFileObjects()) {
+ JCCompilationUnit unit = notYetEntered.remove(file);
+ if (unit != null) {
+ if (roots == null)
+ roots = new ListBuffer<>();
+ roots.append(unit);
+ }
+ }
+ notYetEntered.clear();
+ }
+ }
+ else {
+ for (CompilationUnitTree cu : trees) {
+ if (cu instanceof JCCompilationUnit) {
+ if (roots == null)
+ roots = new ListBuffer<>();
+ roots.append((JCCompilationUnit)cu);
+ notYetEntered.remove(cu.getSourceFile());
+ }
+ else
+ throw new IllegalArgumentException(cu.toString());
+ }
+ }
+
+ if (roots == null) {
+ if (trees == null && !wasInitialized) {
+ compiler.initModules(List.nil());
+ }
+ return List.nil();
+ }
+
+ List units = compiler.initModules(roots.toList());
+
+ try {
+ units = compiler.enterTrees(units);
+
+ if (notYetEntered.isEmpty())
+ compiler.processAnnotations(units);
+
+ ListBuffer elements = new ListBuffer<>();
+ for (JCCompilationUnit unit : units) {
+ boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info",
+ JavaFileObject.Kind.SOURCE);
+ if (isPkgInfo) {
+ elements.append(unit.packge);
+ } else {
+ for (JCTree node : unit.defs) {
+ if (node.hasTag(JCTree.Tag.CLASSDEF)) {
+ JCClassDecl cdef = (JCClassDecl) node;
+ if (cdef.sym != null) // maybe null if errors in anno processing
+ elements.append(cdef.sym);
+ } else if (node.hasTag(JCTree.Tag.MODULEDEF)) {
+ JCModuleDecl mdef = (JCModuleDecl) node;
+ if (mdef.sym != null)
+ elements.append(mdef.sym);
+ }
+ }
+ }
+ }
+ return elements.toList();
+ }
+ finally {
+ compiler.log.flush();
+ }
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Iterable extends Element> analyze() {
+ return handleExceptions(() -> analyze(null), List.nil(), List.nil());
+ }
+
+ /**
+ * 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
+ * @return the elements that were analyzed
+ */
+ // 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 Element> classes) {
+ enter(null); // ensure all classes have been entered
+
+ final ListBuffer results = new ListBuffer<>();
+ try {
+ if (classes == null) {
+ handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results);
+ } else {
+ Filter f = new Filter() {
+ @Override
+ public void process(Env env) {
+ handleFlowResults(compiler.flow(compiler.attribute(env)), results);
+ }
+ };
+ f.run(compiler.todo, classes);
+ }
+ } finally {
+ compiler.log.flush();
+ }
+ return results;
+ }
+ // where
+ private void handleFlowResults(Queue> queue, ListBuffer elems) {
+ for (Env env: queue) {
+ switch (env.tree.getTag()) {
+ case CLASSDEF:
+ JCClassDecl cdef = (JCClassDecl) env.tree;
+ if (cdef.sym != null)
+ elems.append(cdef.sym);
+ break;
+ case MODULEDEF:
+ JCModuleDecl mod = (JCModuleDecl) env.tree;
+ if (mod.sym != null)
+ elems.append(mod.sym);
+ break;
+ case PACKAGEDEF:
+ JCCompilationUnit unit = env.toplevel;
+ if (unit.packge != null)
+ elems.append(unit.packge);
+ break;
+ }
+ }
+ genList.addAll(queue);
+ }
+
+ @Override @DefinedBy(Api.COMPILER_TREE)
+ public Iterable extends JavaFileObject> generate() {
+ return handleExceptions(() -> generate(null), List.nil(), List.nil());
+ }
+
+ /**
+ * 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
+ * @return the files that were generated
+ */
+ public Iterable extends JavaFileObject> generate(Iterable extends Element> classes) {
+ final ListBuffer results = new ListBuffer<>();
+ try {
+ analyze(null); // ensure all classes have been parsed, entered, and analyzed
+
+ if (classes == null) {
+ compiler.generate(compiler.desugar(genList), results);
+ genList.clear();
+ }
+ else {
+ Filter f = new Filter() {
+ @Override
+ public void process(Env env) {
+ compiler.generate(compiler.desugar(ListBuffer.of(env)), results);
+ }
+ };
+ f.run(genList, classes);
+ }
+ if (genList.isEmpty()) {
+ compiler.reportDeferredDiagnostics();
+ cleanup();
+ }
+ }
+ finally {
+ if (compiler != null)
+ compiler.log.flush();
+ }
+ return results;
+ }
+
+ public Iterable extends Tree> pathFor(CompilationUnitTree unit, Tree node) {
+ return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse();
+ }
+
+ public void ensureEntered() {
+ args.allowEmpty();
+ enter(null);
+ }
+
+ abstract class Filter {
+ void run(Queue> list, Iterable extends Element> elements) {
+ Set set = new HashSet<>();
+ for (Element item: elements) {
+ set.add(item);
+ }
+
+ ListBuffer> defer = new ListBuffer<>();
+ while (list.peek() != null) {
+ Env env = list.remove();
+ Symbol test = null;
+
+ if (env.tree.hasTag(Tag.MODULEDEF)) {
+ test = ((JCModuleDecl) env.tree).sym;
+ } else if (env.tree.hasTag(Tag.PACKAGEDEF)) {
+ test = env.toplevel.packge;
+ } else {
+ ClassSymbol csym = env.enclClass.sym;
+ if (csym != null)
+ test = csym.outermostClass();
+ }
+ if (test != null && set.contains(test))
+ process(env);
+ else
+ defer = defer.append(env);
+ }
+
+ list.addAll(defer);
+ }
+
+ abstract void process(Env env);
+ }
+
+ /**
+ * For internal use only. This method will be
+ * removed without warning.
+ * @param expr the type expression to be analyzed
+ * @param scope the scope in which to analyze the type expression
+ * @return the type
+ * @throws IllegalArgumentException if the type expression of null or empty
+ */
+ 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);
+ ParserFactory parserFactory = ParserFactory.instance(context);
+ Attr attr = Attr.instance(context);
+ try {
+ CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length());
+ Parser parser = parserFactory.newParser(buf, false, false, false);
+ JCTree tree = parser.parseType();
+ return attr.attribType(tree, (Symbol.TypeSymbol)scope);
+ } finally {
+ compiler.log.useSource(prev);
+ }
+ }
+
+}