1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/tools/javac/api/JavacTaskImpl.java Sat Dec 01 00:00:00 2007 +0000 1.3 @@ -0,0 +1,536 @@ 1.4 +/* 1.5 + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Sun designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Sun in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.25 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.26 + * have any questions. 1.27 + */ 1.28 + 1.29 +package com.sun.tools.javac.api; 1.30 + 1.31 +import java.io.File; 1.32 +import java.io.IOException; 1.33 +import java.util.*; 1.34 +import java.util.concurrent.atomic.AtomicBoolean; 1.35 + 1.36 +import javax.annotation.processing.Processor; 1.37 +import javax.lang.model.element.Element; 1.38 +import javax.lang.model.element.TypeElement; 1.39 +import javax.lang.model.type.TypeMirror; 1.40 +import javax.tools.*; 1.41 + 1.42 +import com.sun.source.tree.Tree; 1.43 +import com.sun.source.tree.*; 1.44 +import com.sun.source.util.*; 1.45 +import com.sun.tools.javac.code.*; 1.46 +import com.sun.tools.javac.code.Symbol.*; 1.47 +import com.sun.tools.javac.comp.*; 1.48 +import com.sun.tools.javac.main.*; 1.49 +import com.sun.tools.javac.model.*; 1.50 +import com.sun.tools.javac.parser.Parser; 1.51 +import com.sun.tools.javac.parser.Scanner; 1.52 +import com.sun.tools.javac.tree.*; 1.53 +import com.sun.tools.javac.tree.JCTree.*; 1.54 +import com.sun.tools.javac.util.*; 1.55 +import com.sun.tools.javac.util.List; 1.56 +import com.sun.tools.javac.main.JavaCompiler; 1.57 + 1.58 +/** 1.59 + * Provides access to functionality specific to the Sun Java Compiler, javac. 1.60 + * 1.61 + * <p><b>This is NOT part of any API supported by Sun Microsystems. 1.62 + * If you write code that depends on this, you do so at your own 1.63 + * risk. This code and its internal interfaces are subject to change 1.64 + * or deletion without notice.</b></p> 1.65 + * 1.66 + * @author Peter von der Ahé 1.67 + * @author Jonathan Gibbons 1.68 + */ 1.69 +public class JavacTaskImpl extends JavacTask { 1.70 + private JavacTool tool; 1.71 + private Main compilerMain; 1.72 + private JavaCompiler compiler; 1.73 + private String[] args; 1.74 + private Context context; 1.75 + private List<JavaFileObject> fileObjects; 1.76 + private Map<JavaFileObject, JCCompilationUnit> notYetEntered; 1.77 + private ListBuffer<Env<AttrContext>> genList; 1.78 + private TaskListener taskListener; 1.79 + private AtomicBoolean used = new AtomicBoolean(); 1.80 + private Iterable<? extends Processor> processors; 1.81 + 1.82 + private Integer result = null; 1.83 + 1.84 + JavacTaskImpl(JavacTool tool, 1.85 + Main compilerMain, 1.86 + String[] args, 1.87 + Context context, 1.88 + List<JavaFileObject> fileObjects) { 1.89 + this.tool = tool; 1.90 + this.compilerMain = compilerMain; 1.91 + this.args = args; 1.92 + this.context = context; 1.93 + this.fileObjects = fileObjects; 1.94 + // null checks 1.95 + compilerMain.getClass(); 1.96 + args.getClass(); 1.97 + context.getClass(); 1.98 + fileObjects.getClass(); 1.99 + } 1.100 + 1.101 + JavacTaskImpl(JavacTool tool, 1.102 + Main compilerMain, 1.103 + Iterable<String> flags, 1.104 + Context context, 1.105 + Iterable<String> classes, 1.106 + Iterable<? extends JavaFileObject> fileObjects) { 1.107 + this(tool, compilerMain, toArray(flags, classes), context, toList(fileObjects)); 1.108 + } 1.109 + 1.110 + static private String[] toArray(Iterable<String> flags, Iterable<String> classes) { 1.111 + ListBuffer<String> result = new ListBuffer<String>(); 1.112 + if (flags != null) 1.113 + for (String flag : flags) 1.114 + result.append(flag); 1.115 + if (classes != null) 1.116 + for (String cls : classes) 1.117 + result.append(cls); 1.118 + return result.toArray(new String[result.length()]); 1.119 + } 1.120 + 1.121 + static private List<JavaFileObject> toList(Iterable<? extends JavaFileObject> fileObjects) { 1.122 + if (fileObjects == null) 1.123 + return List.nil(); 1.124 + ListBuffer<JavaFileObject> result = new ListBuffer<JavaFileObject>(); 1.125 + for (JavaFileObject fo : fileObjects) 1.126 + result.append(fo); 1.127 + return result.toList(); 1.128 + } 1.129 + 1.130 + public Boolean call() { 1.131 + if (!used.getAndSet(true)) { 1.132 + beginContext(); 1.133 + try { 1.134 + compilerMain.setFatalErrors(true); 1.135 + result = compilerMain.compile(args, context, fileObjects, processors); 1.136 + } finally { 1.137 + endContext(); 1.138 + } 1.139 + compilerMain = null; 1.140 + args = null; 1.141 + context = null; 1.142 + fileObjects = null; 1.143 + return result == 0; 1.144 + } else { 1.145 + throw new IllegalStateException("multiple calls to method 'call'"); 1.146 + } 1.147 + } 1.148 + 1.149 + public void setProcessors(Iterable<? extends Processor> processors) { 1.150 + processors.getClass(); // null check 1.151 + // not mt-safe 1.152 + if (used.get()) 1.153 + throw new IllegalStateException(); 1.154 + this.processors = processors; 1.155 + } 1.156 + 1.157 + public void setLocale(Locale locale) { 1.158 + // locale argument is ignored, see RFE 6443132 1.159 + if (used.get()) 1.160 + throw new IllegalStateException(); 1.161 + } 1.162 + 1.163 + private void prepareCompiler() throws IOException { 1.164 + if (!used.getAndSet(true)) { 1.165 + beginContext(); 1.166 + compilerMain.setOptions(Options.instance(context)); 1.167 + compilerMain.filenames = new ListBuffer<File>(); 1.168 + List<File> filenames = compilerMain.processArgs(CommandLine.parse(args)); 1.169 + if (!filenames.isEmpty()) 1.170 + throw new IllegalArgumentException("Malformed arguments " + filenames.toString(" ")); 1.171 + compiler = JavaCompiler.instance(context); 1.172 + // force the use of the scanner that captures Javadoc comments 1.173 + com.sun.tools.javac.parser.DocCommentScanner.Factory.preRegister(context); 1.174 + compiler.keepComments = true; 1.175 + compiler.genEndPos = true; 1.176 + // NOTE: this value will be updated after annotation processing 1.177 + compiler.initProcessAnnotations(processors); 1.178 + notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>(); 1.179 + for (JavaFileObject file: fileObjects) 1.180 + notYetEntered.put(file, null); 1.181 + genList = new ListBuffer<Env<AttrContext>>(); 1.182 + // endContext will be called when all classes have been generated 1.183 + // TODO: should handle the case after each phase if errors have occurred 1.184 + args = null; 1.185 + } 1.186 + } 1.187 + 1.188 + private void beginContext() { 1.189 + context.put(JavacTaskImpl.class, this); 1.190 + if (context.get(TaskListener.class) != null) 1.191 + context.put(TaskListener.class, (TaskListener)null); 1.192 + if (taskListener != null) 1.193 + context.put(TaskListener.class, wrap(taskListener)); 1.194 + tool.beginContext(context); 1.195 + } 1.196 + // where 1.197 + private TaskListener wrap(final TaskListener tl) { 1.198 + tl.getClass(); // null check 1.199 + return new TaskListener() { 1.200 + public void started(TaskEvent e) { 1.201 + try { 1.202 + tl.started(e); 1.203 + } catch (Throwable t) { 1.204 + throw new ClientCodeException(t); 1.205 + } 1.206 + } 1.207 + 1.208 + public void finished(TaskEvent e) { 1.209 + try { 1.210 + tl.finished(e); 1.211 + } catch (Throwable t) { 1.212 + throw new ClientCodeException(t); 1.213 + } 1.214 + } 1.215 + 1.216 + }; 1.217 + } 1.218 + 1.219 + private void endContext() { 1.220 + tool.endContext(); 1.221 + } 1.222 + 1.223 + /** 1.224 + * Construct a JavaFileObject from the given file. 1.225 + * 1.226 + * <p><b>TODO: this method is useless here</b></p> 1.227 + * 1.228 + * @param file a file 1.229 + * @return a JavaFileObject from the standard file manager. 1.230 + */ 1.231 + public JavaFileObject asJavaFileObject(File file) { 1.232 + JavacFileManager fm = (JavacFileManager)context.get(JavaFileManager.class); 1.233 + return fm.getRegularFile(file); 1.234 + } 1.235 + 1.236 + public void setTaskListener(TaskListener taskListener) { 1.237 + this.taskListener = taskListener; 1.238 + } 1.239 + 1.240 + /** 1.241 + * Parse the specified files returning a list of abstract syntax trees. 1.242 + * 1.243 + * @throws java.io.IOException TODO 1.244 + * @return a list of abstract syntax trees 1.245 + */ 1.246 + public Iterable<? extends CompilationUnitTree> parse() throws IOException { 1.247 + try { 1.248 + prepareCompiler(); 1.249 + List<JCCompilationUnit> units = compiler.parseFiles(fileObjects); 1.250 + for (JCCompilationUnit unit: units) { 1.251 + JavaFileObject file = unit.getSourceFile(); 1.252 + if (notYetEntered.containsKey(file)) 1.253 + notYetEntered.put(file, unit); 1.254 + } 1.255 + return units; 1.256 + } 1.257 + finally { 1.258 + parsed = true; 1.259 + if (compiler != null && compiler.log != null) 1.260 + compiler.log.flush(); 1.261 + } 1.262 + } 1.263 + 1.264 + private boolean parsed = false; 1.265 + 1.266 + /** 1.267 + * Translate all the abstract syntax trees to elements. 1.268 + * 1.269 + * @throws IOException TODO 1.270 + * @return a list of elements corresponding to the top level 1.271 + * classes in the abstract syntax trees 1.272 + */ 1.273 + public Iterable<? extends TypeElement> enter() throws IOException { 1.274 + return enter(null); 1.275 + } 1.276 + 1.277 + /** 1.278 + * Translate the given abstract syntax trees to elements. 1.279 + * 1.280 + * @param trees a list of abstract syntax trees. 1.281 + * @throws java.io.IOException TODO 1.282 + * @return a list of elements corresponding to the top level 1.283 + * classes in the abstract syntax trees 1.284 + */ 1.285 + public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees) 1.286 + throws IOException 1.287 + { 1.288 + prepareCompiler(); 1.289 + 1.290 + ListBuffer<JCCompilationUnit> roots = null; 1.291 + 1.292 + if (trees == null) { 1.293 + // If there are still files which were specified to be compiled 1.294 + // (i.e. in fileObjects) but which have not yet been entered, 1.295 + // then we make sure they have been parsed and add them to the 1.296 + // list to be entered. 1.297 + if (notYetEntered.size() > 0) { 1.298 + if (!parsed) 1.299 + parse(); // TODO would be nice to specify files needed to be parsed 1.300 + for (JavaFileObject file: fileObjects) { 1.301 + JCCompilationUnit unit = notYetEntered.remove(file); 1.302 + if (unit != null) { 1.303 + if (roots == null) 1.304 + roots = new ListBuffer<JCCompilationUnit>(); 1.305 + roots.append(unit); 1.306 + } 1.307 + } 1.308 + notYetEntered.clear(); 1.309 + } 1.310 + } 1.311 + else { 1.312 + for (CompilationUnitTree cu : trees) { 1.313 + if (cu instanceof JCCompilationUnit) { 1.314 + if (roots == null) 1.315 + roots = new ListBuffer<JCCompilationUnit>(); 1.316 + roots.append((JCCompilationUnit)cu); 1.317 + notYetEntered.remove(cu.getSourceFile()); 1.318 + } 1.319 + else 1.320 + throw new IllegalArgumentException(cu.toString()); 1.321 + } 1.322 + } 1.323 + 1.324 + if (roots == null) 1.325 + return List.nil(); 1.326 + 1.327 + try { 1.328 + List<JCCompilationUnit> units = compiler.enterTrees(roots.toList()); 1.329 + 1.330 + if (notYetEntered.isEmpty()) 1.331 + compiler = compiler.processAnnotations(units); 1.332 + 1.333 + ListBuffer<TypeElement> elements = new ListBuffer<TypeElement>(); 1.334 + for (JCCompilationUnit unit : units) { 1.335 + for (JCTree node : unit.defs) 1.336 + if (node.getTag() == JCTree.CLASSDEF) 1.337 + elements.append(((JCTree.JCClassDecl) node).sym); 1.338 + } 1.339 + return elements.toList(); 1.340 + } 1.341 + finally { 1.342 + compiler.log.flush(); 1.343 + } 1.344 + } 1.345 + 1.346 + /** 1.347 + * Complete all analysis. 1.348 + * @throws IOException TODO 1.349 + */ 1.350 + @Override 1.351 + public Iterable<? extends Element> analyze() throws IOException { 1.352 + return analyze(null); 1.353 + } 1.354 + 1.355 + /** 1.356 + * Complete all analysis on the given classes. 1.357 + * This can be used to ensure that all compile time errors are reported. 1.358 + * The classes must have previously been returned from {@link #enter}. 1.359 + * If null is specified, all outstanding classes will be analyzed. 1.360 + * 1.361 + * @param classes a list of class elements 1.362 + */ 1.363 + // This implementation requires that we open up privileges on JavaCompiler. 1.364 + // An alternative implementation would be to move this code to JavaCompiler and 1.365 + // wrap it here 1.366 + public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) throws IOException { 1.367 + enter(null); // ensure all classes have been entered 1.368 + 1.369 + final ListBuffer<Element> results = new ListBuffer<Element>(); 1.370 + try { 1.371 + if (classes == null) { 1.372 + handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results); 1.373 + } else { 1.374 + Filter f = new Filter() { 1.375 + public void process(Env<AttrContext> env) { 1.376 + handleFlowResults(compiler.flow(compiler.attribute(env)), results); 1.377 + } 1.378 + }; 1.379 + f.run(compiler.todo, classes); 1.380 + } 1.381 + } finally { 1.382 + compiler.log.flush(); 1.383 + } 1.384 + return results; 1.385 + } 1.386 + // where 1.387 + private void handleFlowResults(List<Env<AttrContext>> list, ListBuffer<Element> elems) { 1.388 + for (Env<AttrContext> env: list) { 1.389 + switch (env.tree.getTag()) { 1.390 + case JCTree.CLASSDEF: 1.391 + JCClassDecl cdef = (JCClassDecl) env.tree; 1.392 + if (cdef.sym != null) 1.393 + elems.append(cdef.sym); 1.394 + break; 1.395 + case JCTree.TOPLEVEL: 1.396 + JCCompilationUnit unit = (JCCompilationUnit) env.tree; 1.397 + if (unit.packge != null) 1.398 + elems.append(unit.packge); 1.399 + break; 1.400 + } 1.401 + } 1.402 + genList.appendList(list); 1.403 + } 1.404 + 1.405 + 1.406 + /** 1.407 + * Generate code. 1.408 + * @throws IOException TODO 1.409 + */ 1.410 + @Override 1.411 + public Iterable<? extends JavaFileObject> generate() throws IOException { 1.412 + return generate(null); 1.413 + } 1.414 + 1.415 + /** 1.416 + * Generate code corresponding to the given classes. 1.417 + * The classes must have previously been returned from {@link #enter}. 1.418 + * If there are classes outstanding to be analyzed, that will be done before 1.419 + * any classes are generated. 1.420 + * If null is specified, code will be generated for all outstanding classes. 1.421 + * 1.422 + * @param classes a list of class elements 1.423 + */ 1.424 + public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) throws IOException { 1.425 + final ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>(); 1.426 + try { 1.427 + analyze(null); // ensure all classes have been parsed, entered, and analyzed 1.428 + 1.429 + if (classes == null) { 1.430 + compiler.generate(compiler.desugar(genList.toList()), results); 1.431 + genList.clear(); 1.432 + } 1.433 + else { 1.434 + Filter f = new Filter() { 1.435 + public void process(Env<AttrContext> env) { 1.436 + compiler.generate(compiler.desugar(List.of(env)), results); 1.437 + } 1.438 + }; 1.439 + f.run(genList, classes); 1.440 + } 1.441 + if (genList.isEmpty()) { 1.442 + compiler.reportDeferredDiagnostics(); 1.443 + compiler.log.flush(); 1.444 + endContext(); 1.445 + } 1.446 + } 1.447 + finally { 1.448 + compiler.log.flush(); 1.449 + } 1.450 + return results; 1.451 + } 1.452 + 1.453 + public TypeMirror getTypeMirror(Iterable<? extends Tree> path) { 1.454 + // TODO: Should complete attribution if necessary 1.455 + Tree last = null; 1.456 + for (Tree node : path) 1.457 + last = node; 1.458 + return ((JCTree)last).type; 1.459 + } 1.460 + 1.461 + public JavacElements getElements() { 1.462 + if (context == null) 1.463 + throw new IllegalStateException(); 1.464 + return JavacElements.instance(context); 1.465 + } 1.466 + 1.467 + public JavacTypes getTypes() { 1.468 + if (context == null) 1.469 + throw new IllegalStateException(); 1.470 + return JavacTypes.instance(context); 1.471 + } 1.472 + 1.473 + public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) { 1.474 + return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse(); 1.475 + } 1.476 + 1.477 + abstract class Filter { 1.478 + void run(ListBuffer<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) { 1.479 + Set<TypeElement> set = new HashSet<TypeElement>(); 1.480 + for (TypeElement item: classes) 1.481 + set.add(item); 1.482 + 1.483 + List<Env<AttrContext>> defer = List.<Env<AttrContext>>nil(); 1.484 + while (list.nonEmpty()) { 1.485 + Env<AttrContext> env = list.next(); 1.486 + ClassSymbol csym = env.enclClass.sym; 1.487 + if (csym != null && set.contains(csym.outermostClass())) 1.488 + process(env); 1.489 + else 1.490 + defer = defer.prepend(env); 1.491 + } 1.492 + 1.493 + for (List<Env<AttrContext>> l = defer; l.nonEmpty(); l = l.tail) 1.494 + list.prepend(l.head); 1.495 + } 1.496 + 1.497 + abstract void process(Env<AttrContext> env); 1.498 + } 1.499 + 1.500 + /** 1.501 + * For internal use by Sun Microsystems only. This method will be 1.502 + * removed without warning. 1.503 + */ 1.504 + public Context getContext() { 1.505 + return context; 1.506 + } 1.507 + 1.508 + /** 1.509 + * For internal use by Sun Microsystems only. This method will be 1.510 + * removed without warning. 1.511 + */ 1.512 + public void updateContext(Context newContext) { 1.513 + context = newContext; 1.514 + } 1.515 + 1.516 + /** 1.517 + * For internal use by Sun Microsystems only. This method will be 1.518 + * removed without warning. 1.519 + */ 1.520 + public Type parseType(String expr, TypeElement scope) { 1.521 + if (expr == null || expr.equals("")) 1.522 + throw new IllegalArgumentException(); 1.523 + compiler = JavaCompiler.instance(context); 1.524 + JavaFileObject prev = compiler.log.useSource(null); 1.525 + Scanner.Factory scannerFactory = Scanner.Factory.instance(context); 1.526 + Parser.Factory parserFactory = Parser.Factory.instance(context); 1.527 + Attr attr = Attr.instance(context); 1.528 + try { 1.529 + Scanner scanner = scannerFactory.newScanner((expr+"\u0000").toCharArray(), 1.530 + expr.length()); 1.531 + Parser parser = parserFactory.newParser(scanner, false, false); 1.532 + JCTree tree = parser.type(); 1.533 + return attr.attribType(tree, (Symbol.TypeSymbol)scope); 1.534 + } finally { 1.535 + compiler.log.useSource(prev); 1.536 + } 1.537 + } 1.538 + 1.539 +}