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