JavacTaskImpl.java revision 2599:50b448c5be54
1/* 2 * Copyright (c) 2005, 2014, 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 */ 25 26package com.sun.tools.javac.api; 27 28import java.nio.CharBuffer; 29import java.util.*; 30import java.util.concurrent.Callable; 31import java.util.concurrent.atomic.AtomicBoolean; 32 33import javax.annotation.processing.Processor; 34import javax.lang.model.element.Element; 35import javax.lang.model.element.TypeElement; 36import javax.tools.*; 37 38import com.sun.source.tree.*; 39import com.sun.tools.javac.code.*; 40import com.sun.tools.javac.code.Symbol.ClassSymbol; 41import com.sun.tools.javac.comp.*; 42import com.sun.tools.javac.main.*; 43import com.sun.tools.javac.main.JavaCompiler; 44import com.sun.tools.javac.parser.Parser; 45import com.sun.tools.javac.parser.ParserFactory; 46import com.sun.tools.javac.processing.AnnotationProcessingError; 47import com.sun.tools.javac.tree.*; 48import com.sun.tools.javac.tree.JCTree.JCClassDecl; 49import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 50import com.sun.tools.javac.util.*; 51import com.sun.tools.javac.util.List; 52import com.sun.tools.javac.util.Log.PrefixKind; 53import com.sun.tools.javac.util.Log.WriterKind; 54 55/** 56 * Provides access to functionality specific to the JDK Java Compiler, javac. 57 * 58 * <p><b>This is NOT part of any supported API. 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 */ 66public class JavacTaskImpl extends BasicJavacTask { 67 private final Arguments args; 68 private JavaCompiler compiler; 69 private Locale locale; 70 private Map<JavaFileObject, JCCompilationUnit> notYetEntered; 71 private ListBuffer<Env<AttrContext>> genList; 72 private final AtomicBoolean used = new AtomicBoolean(); 73 private Iterable<? extends Processor> processors; 74 75 JavacTaskImpl(Context context) { 76 super(context, true); 77 args = Arguments.instance(context); 78 } 79 80 @Override // @DefinedBy(COMPILER_API) 81 public Boolean call() { 82 return doCall().isOK(); 83 } 84 85 /* Internal version of call exposing Main.Result. */ 86 public Main.Result doCall() { 87 try { 88 return handleExceptions(new Callable<Main.Result>() { 89 @Override 90 public Main.Result call() throws Exception { 91 prepareCompiler(false); 92 compiler.compile(args.getFileObjects(), args.getClassNames(), processors); 93 return (compiler.errorCount() > 0) ? Main.Result.ERROR : Main.Result.OK; // FIXME? 94 } 95 }, Main.Result.SYSERR, Main.Result.ABNORMAL); 96 } finally { 97 try { 98 cleanup(); 99 } catch (ClientCodeException e) { 100 throw new RuntimeException(e.getCause()); 101 } 102 } 103 } 104 105 @Override // @DefinedBy(COMPILER_API) 106 public void setProcessors(Iterable<? extends Processor> processors) { 107 processors.getClass(); // null check 108 // not mt-safe 109 if (used.get()) 110 throw new IllegalStateException(); 111 this.processors = processors; 112 } 113 114 @Override // @DefinedBy(COMPILER_API) 115 public void setLocale(Locale locale) { 116 if (used.get()) 117 throw new IllegalStateException(); 118 this.locale = locale; 119 } 120 121 private <T> T handleExceptions(Callable<T> c, T sysErrorResult, T abnormalErrorResult) { 122 try { 123 return c.call(); 124 } catch (FatalError ex) { 125 Log log = Log.instance(context); 126 Options options = Options.instance(context); 127 log.printRawLines(ex.getMessage()); 128 if (ex.getCause() != null && options.isSet("dev")) { 129 ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE)); 130 } 131 return sysErrorResult; 132 } catch (AnnotationProcessingError | ClientCodeException e) { 133 // AnnotationProcessingError is thrown from JavacProcessingEnvironment, 134 // to forward errors thrown from an annotation processor 135 // ClientCodeException is thrown from ClientCodeWrapper, 136 // to forward errors thrown from user-supplied code for Compiler API 137 // as specified by javax.tools.JavaCompiler#getTask 138 // and javax.tools.JavaCompiler.CompilationTask#call 139 throw new RuntimeException(e.getCause()); 140 } catch (PropagatedException e) { 141 throw e.getCause(); 142 } catch (IllegalStateException e) { 143 throw e; 144 } catch (Exception | Error ex) { 145 // Nasty. If we've already reported an error, compensate 146 // for buggy compiler error recovery by swallowing thrown 147 // exceptions. 148 if (compiler == null || compiler.errorCount() == 0 149 || Options.instance(context).isSet("dev")) { 150 Log log = Log.instance(context); 151 log.printLines(PrefixKind.JAVAC, "msg.bug", JavaCompiler.version()); 152 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 153 } 154 return abnormalErrorResult; 155 } 156 } 157 158 private void prepareCompiler(boolean forParse) { 159 if (used.getAndSet(true)) { 160 if (compiler == null) 161 throw new PropagatedException(new IllegalStateException()); 162 } else { 163 args.validate(); 164 165 //initialize compiler's default locale 166 context.put(Locale.class, locale); 167 168 // hack 169 JavacMessages messages = context.get(JavacMessages.messagesKey); 170 if (messages != null && !messages.getCurrentLocale().equals(locale)) 171 messages.setCurrentLocale(locale); 172 173 initPlugins(args.getPluginOpts()); 174 initDocLint(args.getDocLintOpts()); 175 176 // init JavaCompiler and queues 177 compiler = JavaCompiler.instance(context); 178 compiler.keepComments = true; 179 compiler.genEndPos = true; 180 notYetEntered = new HashMap<>(); 181 if (forParse) { 182 compiler.initProcessAnnotations(processors); 183 for (JavaFileObject file: args.getFileObjects()) 184 notYetEntered.put(file, null); 185 genList = new ListBuffer<>(); 186 } 187 } 188 } 189 190 <T> String toString(Iterable<T> items, String sep) { 191 String currSep = ""; 192 StringBuilder sb = new StringBuilder(); 193 for (T item: items) { 194 sb.append(currSep); 195 sb.append(item.toString()); 196 currSep = sep; 197 } 198 return sb.toString(); 199 } 200 201 void cleanup() { 202 if (compiler != null) 203 compiler.close(); 204 compiler = null; 205 context = null; 206 notYetEntered = null; 207 } 208 209 @Override // @DefinedBy(TREE_API) 210 public Iterable<? extends CompilationUnitTree> parse() { 211 return handleExceptions(new Callable<Iterable<? extends CompilationUnitTree>>() { 212 @Override 213 public Iterable<? extends CompilationUnitTree> call() { 214 return parseInternal(); 215 } 216 }, List.<CompilationUnitTree>nil(), List.<CompilationUnitTree>nil()); 217 } 218 219 private Iterable<? extends CompilationUnitTree> parseInternal() { 220 try { 221 prepareCompiler(true); 222 List<JCCompilationUnit> units = compiler.parseFiles(args.getFileObjects()); 223 for (JCCompilationUnit unit: units) { 224 JavaFileObject file = unit.getSourceFile(); 225 if (notYetEntered.containsKey(file)) 226 notYetEntered.put(file, unit); 227 } 228 return units; 229 } 230 finally { 231 parsed = true; 232 if (compiler != null && compiler.log != null) 233 compiler.log.flush(); 234 } 235 } 236 237 private boolean parsed = false; 238 239 /** 240 * Translate all the abstract syntax trees to elements. 241 * 242 * @return a list of elements corresponding to the top level 243 * classes in the abstract syntax trees 244 */ 245 public Iterable<? extends TypeElement> enter() { 246 return enter(null); 247 } 248 249 /** 250 * Translate the given abstract syntax trees to elements. 251 * 252 * @param trees a list of abstract syntax trees. 253 * @return a list of elements corresponding to the top level 254 * classes in the abstract syntax trees 255 */ 256 public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees) 257 { 258 if (trees == null && notYetEntered != null && notYetEntered.isEmpty()) 259 return List.nil(); 260 261 prepareCompiler(true); 262 263 ListBuffer<JCCompilationUnit> roots = null; 264 265 if (trees == null) { 266 // If there are still files which were specified to be compiled 267 // (i.e. in fileObjects) but which have not yet been entered, 268 // then we make sure they have been parsed and add them to the 269 // list to be entered. 270 if (notYetEntered.size() > 0) { 271 if (!parsed) 272 parseInternal(); // TODO would be nice to specify files needed to be parsed 273 for (JavaFileObject file: args.getFileObjects()) { 274 JCCompilationUnit unit = notYetEntered.remove(file); 275 if (unit != null) { 276 if (roots == null) 277 roots = new ListBuffer<>(); 278 roots.append(unit); 279 } 280 } 281 notYetEntered.clear(); 282 } 283 } 284 else { 285 for (CompilationUnitTree cu : trees) { 286 if (cu instanceof JCCompilationUnit) { 287 if (roots == null) 288 roots = new ListBuffer<>(); 289 roots.append((JCCompilationUnit)cu); 290 notYetEntered.remove(cu.getSourceFile()); 291 } 292 else 293 throw new IllegalArgumentException(cu.toString()); 294 } 295 } 296 297 if (roots == null) 298 return List.nil(); 299 300 try { 301 List<JCCompilationUnit> units = compiler.enterTrees(roots.toList()); 302 303 if (notYetEntered.isEmpty()) 304 compiler.processAnnotations(units); 305 306 ListBuffer<TypeElement> elements = new ListBuffer<>(); 307 for (JCCompilationUnit unit : units) { 308 for (JCTree node : unit.defs) { 309 if (node.hasTag(JCTree.Tag.CLASSDEF)) { 310 JCClassDecl cdef = (JCClassDecl) node; 311 if (cdef.sym != null) // maybe null if errors in anno processing 312 elements.append(cdef.sym); 313 } 314 } 315 } 316 return elements.toList(); 317 } 318 finally { 319 compiler.log.flush(); 320 } 321 } 322 323 @Override // @DefinedBy(TREE_API) 324 public Iterable<? extends Element> analyze() { 325 return handleExceptions(new Callable<Iterable<? extends Element>>() { 326 @Override 327 public Iterable<? extends Element> call() { 328 return analyze(null); 329 } 330 }, List.<Element>nil(), List.<Element>nil()); 331 } 332 333 /** 334 * Complete all analysis on the given classes. 335 * This can be used to ensure that all compile time errors are reported. 336 * The classes must have previously been returned from {@link #enter}. 337 * If null is specified, all outstanding classes will be analyzed. 338 * 339 * @param classes a list of class elements 340 * @return the elements that were analyzed 341 */ 342 // This implementation requires that we open up privileges on JavaCompiler. 343 // An alternative implementation would be to move this code to JavaCompiler and 344 // wrap it here 345 public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) { 346 enter(null); // ensure all classes have been entered 347 348 final ListBuffer<Element> results = new ListBuffer<>(); 349 try { 350 if (classes == null) { 351 handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results); 352 } else { 353 Filter f = new Filter() { 354 @Override 355 public void process(Env<AttrContext> env) { 356 handleFlowResults(compiler.flow(compiler.attribute(env)), results); 357 } 358 }; 359 f.run(compiler.todo, classes); 360 } 361 } finally { 362 compiler.log.flush(); 363 } 364 return results; 365 } 366 // where 367 private void handleFlowResults(Queue<Env<AttrContext>> queue, ListBuffer<Element> elems) { 368 for (Env<AttrContext> env: queue) { 369 switch (env.tree.getTag()) { 370 case CLASSDEF: 371 JCClassDecl cdef = (JCClassDecl) env.tree; 372 if (cdef.sym != null) 373 elems.append(cdef.sym); 374 break; 375 case TOPLEVEL: 376 JCCompilationUnit unit = (JCCompilationUnit) env.tree; 377 if (unit.packge != null) 378 elems.append(unit.packge); 379 break; 380 } 381 } 382 genList.addAll(queue); 383 } 384 385 @Override // @DefinedBy(TREE_API) 386 public Iterable<? extends JavaFileObject> generate() { 387 return handleExceptions(new Callable<Iterable<? extends JavaFileObject>>() { 388 @Override 389 public Iterable<? extends JavaFileObject> call() { 390 return generate(null); 391 } 392 }, List.<JavaFileObject>nil(), List.<JavaFileObject>nil()); 393 } 394 395 /** 396 * Generate code corresponding to the given classes. 397 * The classes must have previously been returned from {@link #enter}. 398 * If there are classes outstanding to be analyzed, that will be done before 399 * any classes are generated. 400 * If null is specified, code will be generated for all outstanding classes. 401 * 402 * @param classes a list of class elements 403 * @return the files that were generated 404 */ 405 public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) { 406 final ListBuffer<JavaFileObject> results = new ListBuffer<>(); 407 try { 408 analyze(null); // ensure all classes have been parsed, entered, and analyzed 409 410 if (classes == null) { 411 compiler.generate(compiler.desugar(genList), results); 412 genList.clear(); 413 } 414 else { 415 Filter f = new Filter() { 416 @Override 417 public void process(Env<AttrContext> env) { 418 compiler.generate(compiler.desugar(ListBuffer.of(env)), results); 419 } 420 }; 421 f.run(genList, classes); 422 } 423 if (genList.isEmpty()) { 424 compiler.reportDeferredDiagnostics(); 425 cleanup(); 426 } 427 } 428 finally { 429 if (compiler != null) 430 compiler.log.flush(); 431 } 432 return results; 433 } 434 435 public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) { 436 return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse(); 437 } 438 439 abstract class Filter { 440 void run(Queue<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) { 441 Set<TypeElement> set = new HashSet<>(); 442 for (TypeElement item: classes) 443 set.add(item); 444 445 ListBuffer<Env<AttrContext>> defer = new ListBuffer<>(); 446 while (list.peek() != null) { 447 Env<AttrContext> env = list.remove(); 448 ClassSymbol csym = env.enclClass.sym; 449 if (csym != null && set.contains(csym.outermostClass())) 450 process(env); 451 else 452 defer = defer.append(env); 453 } 454 455 list.addAll(defer); 456 } 457 458 abstract void process(Env<AttrContext> env); 459 } 460 461 /** 462 * For internal use only. This method will be 463 * removed without warning. 464 * @param expr the type expression to be analyzed 465 * @param scope the scope in which to analyze the type expression 466 * @return the type 467 * @throws IllegalArgumentException if the type expression of null or empty 468 */ 469 public Type parseType(String expr, TypeElement scope) { 470 if (expr == null || expr.equals("")) 471 throw new IllegalArgumentException(); 472 compiler = JavaCompiler.instance(context); 473 JavaFileObject prev = compiler.log.useSource(null); 474 ParserFactory parserFactory = ParserFactory.instance(context); 475 Attr attr = Attr.instance(context); 476 try { 477 CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length()); 478 Parser parser = parserFactory.newParser(buf, false, false, false); 479 JCTree tree = parser.parseType(); 480 return attr.attribType(tree, (Symbol.TypeSymbol)scope); 481 } finally { 482 compiler.log.useSource(prev); 483 } 484 } 485 486} 487