001: /*
002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.javac.api;
027:
028: import java.io.File;
029: import java.io.IOException;
030: import java.util.*;
031: import java.util.concurrent.atomic.AtomicBoolean;
032:
033: import javax.annotation.processing.Processor;
034: import javax.lang.model.element.Element;
035: import javax.lang.model.element.TypeElement;
036: import javax.lang.model.type.TypeMirror;
037: import javax.tools.*;
038:
039: import com.sun.source.tree.Tree;
040: import com.sun.source.tree.*;
041: import com.sun.source.util.*;
042: import com.sun.tools.javac.code.*;
043: import com.sun.tools.javac.code.Symbol.*;
044: import com.sun.tools.javac.comp.*;
045: import com.sun.tools.javac.main.*;
046: import com.sun.tools.javac.model.*;
047: import com.sun.tools.javac.parser.Parser;
048: import com.sun.tools.javac.parser.Scanner;
049: import com.sun.tools.javac.tree.*;
050: import com.sun.tools.javac.tree.JCTree.*;
051: import com.sun.tools.javac.util.*;
052: import com.sun.tools.javac.util.List;
053: import com.sun.tools.javac.main.JavaCompiler;
054:
055: /**
056: * Provides access to functionality specific to the Sun Java Compiler, javac.
057: *
058: * <p><b>This is NOT part of any API supported by Sun Microsystems.
059: * If you write code that depends on this, you do so at your own
060: * risk. This code and its internal interfaces are subject to change
061: * or deletion without notice.</b></p>
062: *
063: * @author Peter von der Ahé
064: * @author Jonathan Gibbons
065: */
066: public class JavacTaskImpl extends JavacTask {
067: private JavacTool tool;
068: private Main compilerMain;
069: private JavaCompiler compiler;
070: private String[] args;
071: private Context context;
072: private List<JavaFileObject> fileObjects;
073: private Map<JavaFileObject, JCCompilationUnit> notYetEntered;
074: private ListBuffer<Env<AttrContext>> genList;
075: private TaskListener taskListener;
076: private AtomicBoolean used = new AtomicBoolean();
077: private Iterable<? extends Processor> processors;
078:
079: private Integer result = null;
080:
081: JavacTaskImpl(JavacTool tool, Main compilerMain, String[] args,
082: Context context, List<JavaFileObject> fileObjects) {
083: this .tool = tool;
084: this .compilerMain = compilerMain;
085: this .args = args;
086: this .context = context;
087: this .fileObjects = fileObjects;
088: // null checks
089: compilerMain.getClass();
090: args.getClass();
091: context.getClass();
092: fileObjects.getClass();
093: }
094:
095: JavacTaskImpl(JavacTool tool, Main compilerMain,
096: Iterable<String> flags, Context context,
097: Iterable<String> classes,
098: Iterable<? extends JavaFileObject> fileObjects) {
099: this (tool, compilerMain, toArray(flags, classes), context,
100: toList(fileObjects));
101: }
102:
103: static private String[] toArray(Iterable<String> flags,
104: Iterable<String> classes) {
105: ListBuffer<String> result = new ListBuffer<String>();
106: if (flags != null)
107: for (String flag : flags)
108: result.append(flag);
109: if (classes != null)
110: for (String cls : classes)
111: result.append(cls);
112: return result.toArray(new String[result.length()]);
113: }
114:
115: static private List<JavaFileObject> toList(
116: Iterable<? extends JavaFileObject> fileObjects) {
117: if (fileObjects == null)
118: return List.nil();
119: ListBuffer<JavaFileObject> result = new ListBuffer<JavaFileObject>();
120: for (JavaFileObject fo : fileObjects)
121: result.append(fo);
122: return result.toList();
123: }
124:
125: public Boolean call() {
126: if (!used.getAndSet(true)) {
127: beginContext();
128: try {
129: compilerMain.setFatalErrors(true);
130: result = compilerMain.compile(args, context,
131: fileObjects, processors);
132: } finally {
133: endContext();
134: }
135: compilerMain = null;
136: args = null;
137: context = null;
138: fileObjects = null;
139: return result == 0;
140: } else {
141: throw new IllegalStateException(
142: "multiple calls to method 'call'");
143: }
144: }
145:
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: }
153:
154: public void setLocale(Locale locale) {
155: // locale argument is ignored, see RFE 6443132
156: if (used.get())
157: throw new IllegalStateException();
158: }
159:
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
166: .parse(args));
167: if (!filenames.isEmpty())
168: throw new IllegalArgumentException(
169: "Malformed arguments "
170: + filenames.toString(" "));
171: compiler = JavaCompiler.instance(context);
172: // force the use of the scanner that captures Javadoc comments
173: com.sun.tools.javac.parser.DocCommentScanner.Factory
174: .preRegister(context);
175: compiler.keepComments = true;
176: compiler.genEndPos = true;
177: // NOTE: this value will be updated after annotation processing
178: compiler.initProcessAnnotations(processors);
179: notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>();
180: for (JavaFileObject file : fileObjects)
181: notYetEntered.put(file, null);
182: genList = new ListBuffer<Env<AttrContext>>();
183: // endContext will be called when all classes have been generated
184: // TODO: should handle the case after each phase if errors have occurred
185: args = null;
186: }
187: }
188:
189: private void beginContext() {
190: context.put(JavacTaskImpl.class, this );
191: if (context.get(TaskListener.class) != null)
192: context.put(TaskListener.class, (TaskListener) null);
193: if (taskListener != null)
194: context.put(TaskListener.class, wrap(taskListener));
195: tool.beginContext(context);
196: }
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: }
209:
210: public void finished(TaskEvent e) {
211: try {
212: tl.finished(e);
213: } catch (Throwable t) {
214: throw new ClientCodeException(t);
215: }
216: }
217:
218: };
219: }
220:
221: private void endContext() {
222: tool.endContext();
223: }
224:
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
235: .get(JavaFileManager.class);
236: return fm.getRegularFile(file);
237: }
238:
239: public void setTaskListener(TaskListener taskListener) {
240: this .taskListener = taskListener;
241: }
242:
243: /**
244: * Parse the specified files returning a list of abstract syntax trees.
245: *
246: * @throws java.io.IOException TODO
247: * @return a list of abstract syntax trees
248: */
249: public Iterable<? extends CompilationUnitTree> parse()
250: throws IOException {
251: try {
252: prepareCompiler();
253: List<JCCompilationUnit> units = compiler
254: .parseFiles(fileObjects);
255: for (JCCompilationUnit unit : units) {
256: JavaFileObject file = unit.getSourceFile();
257: if (notYetEntered.containsKey(file))
258: notYetEntered.put(file, unit);
259: }
260: return units;
261: } finally {
262: parsed = true;
263: if (compiler != null && compiler.log != null)
264: compiler.log.flush();
265: }
266: }
267:
268: private boolean parsed = false;
269:
270: /**
271: * Translate all the abstract syntax trees to elements.
272: *
273: * @throws IOException TODO
274: * @return a list of elements corresponding to the top level
275: * classes in the abstract syntax trees
276: */
277: public Iterable<? extends TypeElement> enter() throws IOException {
278: return enter(null);
279: }
280:
281: /**
282: * Translate the given abstract syntax trees to elements.
283: *
284: * @param trees a list of abstract syntax trees.
285: * @throws java.io.IOException TODO
286: * @return a list of elements corresponding to the top level
287: * classes in the abstract syntax trees
288: */
289: public Iterable<? extends TypeElement> enter(
290: Iterable<? extends CompilationUnitTree> trees)
291: throws IOException {
292: prepareCompiler();
293:
294: ListBuffer<JCCompilationUnit> roots = null;
295:
296: if (trees == null) {
297: // If there are still files which were specified to be compiled
298: // (i.e. in fileObjects) but which have not yet been entered,
299: // then we make sure they have been parsed and add them to the
300: // list to be entered.
301: if (notYetEntered.size() > 0) {
302: if (!parsed)
303: parse(); // TODO would be nice to specify files needed to be parsed
304: for (JavaFileObject file : fileObjects) {
305: JCCompilationUnit unit = notYetEntered.remove(file);
306: if (unit != null) {
307: if (roots == null)
308: roots = new ListBuffer<JCCompilationUnit>();
309: roots.append(unit);
310: }
311: }
312: notYetEntered.clear();
313: }
314: } else {
315: for (CompilationUnitTree cu : trees) {
316: if (cu instanceof JCCompilationUnit) {
317: if (roots == null)
318: roots = new ListBuffer<JCCompilationUnit>();
319: roots.append((JCCompilationUnit) cu);
320: notYetEntered.remove(cu.getSourceFile());
321: } else
322: throw new IllegalArgumentException(cu.toString());
323: }
324: }
325:
326: if (roots == null)
327: return List.nil();
328:
329: try {
330: List<JCCompilationUnit> units = compiler.enterTrees(roots
331: .toList());
332:
333: if (notYetEntered.isEmpty())
334: compiler = compiler.processAnnotations(units);
335:
336: ListBuffer<TypeElement> elements = new ListBuffer<TypeElement>();
337: for (JCCompilationUnit unit : units) {
338: for (JCTree node : unit.defs)
339: if (node.getTag() == JCTree.CLASSDEF)
340: elements
341: .append(((JCTree.JCClassDecl) node).sym);
342: }
343: return elements.toList();
344: } finally {
345: compiler.log.flush();
346: }
347: }
348:
349: /**
350: * Complete all analysis.
351: * @throws IOException TODO
352: */
353: @Override
354: public Iterable<? extends Element> analyze() throws IOException {
355: return analyze(null);
356: }
357:
358: /**
359: * Complete all analysis on the given classes.
360: * This can be used to ensure that all compile time errors are reported.
361: * The classes must have previously been returned from {@link #enter}.
362: * If null is specified, all outstanding classes will be analyzed.
363: *
364: * @param classes a list of class elements
365: */
366: // This implementation requires that we open up privileges on JavaCompiler.
367: // An alternative implementation would be to move this code to JavaCompiler and
368: // wrap it here
369: public Iterable<? extends Element> analyze(
370: Iterable<? extends TypeElement> classes) throws IOException {
371: enter(null); // ensure all classes have been entered
372:
373: final ListBuffer<Element> results = new ListBuffer<Element>();
374: try {
375: if (classes == null) {
376: handleFlowResults(compiler.flow(compiler
377: .attribute(compiler.todo)), results);
378: } else {
379: Filter f = new Filter() {
380: public void process(Env<AttrContext> env) {
381: handleFlowResults(compiler.flow(compiler
382: .attribute(env)), results);
383: }
384: };
385: f.run(compiler.todo, classes);
386: }
387: } finally {
388: compiler.log.flush();
389: }
390: return results;
391: }
392:
393: // where
394: private void handleFlowResults(List<Env<AttrContext>> list,
395: ListBuffer<Element> elems) {
396: for (Env<AttrContext> env : list) {
397: switch (env.tree.getTag()) {
398: case JCTree.CLASSDEF:
399: JCClassDecl cdef = (JCClassDecl) env.tree;
400: if (cdef.sym != null)
401: elems.append(cdef.sym);
402: break;
403: case JCTree.TOPLEVEL:
404: JCCompilationUnit unit = (JCCompilationUnit) env.tree;
405: if (unit.packge != null)
406: elems.append(unit.packge);
407: break;
408: }
409: }
410: genList.appendList(list);
411: }
412:
413: /**
414: * Generate code.
415: * @throws IOException TODO
416: */
417: @Override
418: public Iterable<? extends JavaFileObject> generate()
419: throws IOException {
420: return generate(null);
421: }
422:
423: /**
424: * Generate code corresponding to the given classes.
425: * The classes must have previously been returned from {@link #enter}.
426: * If there are classes outstanding to be analyzed, that will be done before
427: * any classes are generated.
428: * If null is specified, code will be generated for all outstanding classes.
429: *
430: * @param classes a list of class elements
431: */
432: public Iterable<? extends JavaFileObject> generate(
433: Iterable<? extends TypeElement> classes) throws IOException {
434: final ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>();
435: try {
436: analyze(null); // ensure all classes have been parsed, entered, and analyzed
437:
438: if (classes == null) {
439: compiler.generate(compiler.desugar(genList.toList()),
440: results);
441: genList.clear();
442: } else {
443: Filter f = new Filter() {
444: public void process(Env<AttrContext> env) {
445: compiler.generate(compiler
446: .desugar(List.of(env)), results);
447: }
448: };
449: f.run(genList, classes);
450: }
451: if (genList.isEmpty()) {
452: compiler.reportDeferredDiagnostics();
453: compiler.log.flush();
454: endContext();
455: }
456: } finally {
457: compiler.log.flush();
458: }
459: return results;
460: }
461:
462: public TypeMirror getTypeMirror(Iterable<? extends Tree> path) {
463: // TODO: Should complete attribution if necessary
464: Tree last = null;
465: for (Tree node : path)
466: last = node;
467: return ((JCTree) last).type;
468: }
469:
470: public JavacElements getElements() {
471: if (context == null)
472: throw new IllegalStateException();
473: return JavacElements.instance(context);
474: }
475:
476: public JavacTypes getTypes() {
477: if (context == null)
478: throw new IllegalStateException();
479: return JavacTypes.instance(context);
480: }
481:
482: public Iterable<? extends Tree> pathFor(CompilationUnitTree unit,
483: Tree node) {
484: return TreeInfo.pathFor((JCTree) node,
485: (JCTree.JCCompilationUnit) unit).reverse();
486: }
487:
488: abstract class Filter {
489: void run(ListBuffer<Env<AttrContext>> list,
490: Iterable<? extends TypeElement> classes) {
491: Set<TypeElement> set = new HashSet<TypeElement>();
492: for (TypeElement item : classes)
493: set.add(item);
494:
495: List<Env<AttrContext>> defer = List
496: .<Env<AttrContext>> nil();
497: while (list.nonEmpty()) {
498: Env<AttrContext> env = list.next();
499: ClassSymbol csym = env.enclClass.sym;
500: if (csym != null && set.contains(csym.outermostClass()))
501: process(env);
502: else
503: defer = defer.prepend(env);
504: }
505:
506: for (List<Env<AttrContext>> l = defer; l.nonEmpty(); l = l.tail)
507: list.prepend(l.head);
508: }
509:
510: abstract void process(Env<AttrContext> env);
511: }
512:
513: /**
514: * For internal use by Sun Microsystems only. This method will be
515: * removed without warning.
516: */
517: public Context getContext() {
518: return context;
519: }
520:
521: /**
522: * For internal use by Sun Microsystems only. This method will be
523: * removed without warning.
524: */
525: public void updateContext(Context newContext) {
526: context = newContext;
527: }
528:
529: /**
530: * For internal use by Sun Microsystems only. This method will be
531: * removed without warning.
532: */
533: public Type parseType(String expr, TypeElement scope) {
534: if (expr == null || expr.equals(""))
535: throw new IllegalArgumentException();
536: compiler = JavaCompiler.instance(context);
537: JavaFileObject prev = compiler.log.useSource(null);
538: Scanner.Factory scannerFactory = Scanner.Factory
539: .instance(context);
540: Parser.Factory parserFactory = Parser.Factory.instance(context);
541: Attr attr = Attr.instance(context);
542: try {
543: Scanner scanner = scannerFactory.newScanner(
544: (expr + "\u0000").toCharArray(), expr.length());
545: Parser parser = parserFactory.newParser(scanner, false,
546: false);
547: JCTree tree = parser.type();
548: return attr.attribType(tree, (Symbol.TypeSymbol) scope);
549: } finally {
550: compiler.log.useSource(prev);
551: }
552: }
553:
554: }
|