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