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