JavacTaskImpl.java revision 2693:c36403059804
114456Ssos/* 2230132Suqs * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. 314456Ssos * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 414456Ssos * 514456Ssos * This code is free software; you can redistribute it and/or modify it 614456Ssos * under the terms of the GNU General Public License version 2 only, as 714456Ssos * published by the Free Software Foundation. Oracle designates this 814456Ssos * particular file as subject to the "Classpath" exception as provided 914456Ssos * by Oracle in the LICENSE file that accompanied this code. 1014456Ssos * 1114456Ssos * This code is distributed in the hope that it will be useful, but WITHOUT 1214456Ssos * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1314456Ssos * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1414456Ssos * version 2 for more details (a copy is included in the LICENSE file that 1597748Sschweikh * accompanied this code). 1614456Ssos * 1714456Ssos * You should have received a copy of the GNU General Public License version 1814456Ssos * 2 along with this work; if not, write to the Free Software Foundation, 1914456Ssos * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2014456Ssos * 2114456Ssos * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2214456Ssos * or visit www.oracle.com if you need additional information or have any 2314456Ssos * questions. 2414456Ssos */ 2514456Ssos 2614456Ssospackage com.sun.tools.javac.api; 2714456Ssos 2850477Speterimport java.io.IOException; 2914456Ssosimport java.nio.CharBuffer; 3014456Ssosimport java.util.*; 3125984Sjdpimport java.util.concurrent.Callable; 32186668Sobrienimport java.util.concurrent.atomic.AtomicBoolean; 3314456Ssos 3425984Sjdpimport javax.annotation.processing.Processor; 3514456Ssosimport javax.lang.model.element.Element; 3655205Speterimport javax.lang.model.element.TypeElement; 3714456Ssosimport javax.tools.*; 38186668Sobrien 3914456Ssosimport com.sun.source.tree.*; 40100384Speterimport com.sun.tools.javac.code.*; 41100384Speterimport com.sun.tools.javac.code.Symbol.ClassSymbol; 4214456Ssosimport com.sun.tools.javac.comp.*; 4314456Ssosimport com.sun.tools.javac.main.*; 4414456Ssosimport com.sun.tools.javac.main.JavaCompiler; 4514456Ssosimport com.sun.tools.javac.parser.Parser; 4614456Ssosimport com.sun.tools.javac.parser.ParserFactory; 47153504Smarcelimport com.sun.tools.javac.processing.AnnotationProcessingError; 48153504Smarcelimport com.sun.tools.javac.tree.*; 49153504Smarcelimport com.sun.tools.javac.tree.JCTree.JCClassDecl; 50153504Smarcelimport com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 51153504Smarcelimport com.sun.tools.javac.util.*; 52153504Smarcelimport com.sun.tools.javac.util.DefinedBy.Api; 53153504Smarcelimport com.sun.tools.javac.util.List; 54153504Smarcelimport com.sun.tools.javac.util.Log.PrefixKind; 55108696Sjakeimport com.sun.tools.javac.util.Log.WriterKind; 5614456Ssos 5714456Ssos/** 58189771Sdchagin * Provides access to functionality specific to the JDK Java Compiler, javac. 59189771Sdchagin * 60189771Sdchagin * <p><b>This is NOT part of any supported API. 61196512Sbz * If you write code that depends on this, you do so at your own 62196512Sbz * risk. This code and its internal interfaces are subject to change 63210446Skib * or deletion without notice.</b></p> 64210446Skib * 65189771Sdchagin * @author Peter von der Ahé 66189771Sdchagin * @author Jonathan Gibbons 67189771Sdchagin */ 6859342Sobrienpublic class JavacTaskImpl extends BasicJavacTask { 69100384Speter private final Arguments args; 7072999Sobrien private JavaCompiler compiler; 7159342Sobrien private JavaFileManager fileManager; 7259342Sobrien private Locale locale; 73123742Speter private Map<JavaFileObject, JCCompilationUnit> notYetEntered; 74123742Speter private ListBuffer<Env<AttrContext>> genList; 75190708Sdchagin private final AtomicBoolean used = new AtomicBoolean(); 76189771Sdchagin private Iterable<? extends Processor> processors; 77196653Sbz 78196653Sbz JavacTaskImpl(Context context) { 79196653Sbz super(context, true); 80108696Sjake args = Arguments.instance(context); 8114456Ssos fileManager = context.get(JavaFileManager.class); 82108696Sjake } 83108696Sjake 8414456Ssos @Override @DefinedBy(Api.COMPILER) 85186668Sobrien public Boolean call() { 8636735Sdfr return doCall().isOK(); 87108696Sjake } 88108696Sjake 89108696Sjake /* Internal version of call exposing Main.Result. */ 90108696Sjake public Main.Result doCall() { 91204552Salfred try { 9236735Sdfr return handleExceptions(new Callable<Main.Result>() { 93133464Smarcel @Override 94133464Smarcel public Main.Result call() throws Exception { 95133464Smarcel prepareCompiler(false); 96186680Sbz compiler.compile(args.getFileObjects(), args.getClassNames(), processors); 97189771Sdchagin return (compiler.errorCount() > 0) ? Main.Result.ERROR : Main.Result.OK; // FIXME? 98196512Sbz } 9955205Speter }, Main.Result.SYSERR, Main.Result.ABNORMAL); 10025984Sjdp } finally { 10125984Sjdp try { 102 cleanup(); 103 } catch (ClientCodeException e) { 104 throw new RuntimeException(e.getCause()); 105 } 106 } 107 } 108 109 @Override @DefinedBy(Api.COMPILER) 110 public void setProcessors(Iterable<? extends Processor> processors) { 111 processors.getClass(); // null check 112 // not mt-safe 113 if (used.get()) 114 throw new IllegalStateException(); 115 this.processors = processors; 116 } 117 118 @Override @DefinedBy(Api.COMPILER) 119 public void setLocale(Locale locale) { 120 if (used.get()) 121 throw new IllegalStateException(); 122 this.locale = locale; 123 } 124 125 private <T> T handleExceptions(Callable<T> c, T sysErrorResult, T abnormalErrorResult) { 126 try { 127 return c.call(); 128 } catch (FatalError ex) { 129 Log log = Log.instance(context); 130 Options options = Options.instance(context); 131 log.printRawLines(ex.getMessage()); 132 if (ex.getCause() != null && options.isSet("dev")) { 133 ex.getCause().printStackTrace(log.getWriter(WriterKind.NOTICE)); 134 } 135 return sysErrorResult; 136 } catch (AnnotationProcessingError | ClientCodeException e) { 137 // AnnotationProcessingError is thrown from JavacProcessingEnvironment, 138 // to forward errors thrown from an annotation processor 139 // ClientCodeException is thrown from ClientCodeWrapper, 140 // to forward errors thrown from user-supplied code for Compiler API 141 // as specified by javax.tools.JavaCompiler#getTask 142 // and javax.tools.JavaCompiler.CompilationTask#call 143 throw new RuntimeException(e.getCause()); 144 } catch (PropagatedException e) { 145 throw e.getCause(); 146 } catch (IllegalStateException e) { 147 throw e; 148 } catch (Exception | Error ex) { 149 // Nasty. If we've already reported an error, compensate 150 // for buggy compiler error recovery by swallowing thrown 151 // exceptions. 152 if (compiler == null || compiler.errorCount() == 0 153 || Options.instance(context).isSet("dev")) { 154 Log log = Log.instance(context); 155 log.printLines(PrefixKind.JAVAC, "msg.bug", JavaCompiler.version()); 156 ex.printStackTrace(log.getWriter(WriterKind.NOTICE)); 157 } 158 return abnormalErrorResult; 159 } 160 } 161 162 private void prepareCompiler(boolean forParse) { 163 if (used.getAndSet(true)) { 164 if (compiler == null) 165 throw new PropagatedException(new IllegalStateException()); 166 } else { 167 args.validate(); 168 169 //initialize compiler's default locale 170 context.put(Locale.class, locale); 171 172 // hack 173 JavacMessages messages = context.get(JavacMessages.messagesKey); 174 if (messages != null && !messages.getCurrentLocale().equals(locale)) 175 messages.setCurrentLocale(locale); 176 177 initPlugins(args.getPluginOpts()); 178 initDocLint(args.getDocLintOpts()); 179 180 // init JavaCompiler and queues 181 compiler = JavaCompiler.instance(context); 182 compiler.keepComments = true; 183 compiler.genEndPos = true; 184 notYetEntered = new HashMap<>(); 185 if (forParse) { 186 compiler.initProcessAnnotations(processors); 187 for (JavaFileObject file: args.getFileObjects()) 188 notYetEntered.put(file, null); 189 genList = new ListBuffer<>(); 190 } 191 } 192 } 193 194 <T> String toString(Iterable<T> items, String sep) { 195 String currSep = ""; 196 StringBuilder sb = new StringBuilder(); 197 for (T item: items) { 198 sb.append(currSep); 199 sb.append(item.toString()); 200 currSep = sep; 201 } 202 return sb.toString(); 203 } 204 205 void cleanup() { 206 if (compiler != null) 207 compiler.close(); 208 if (fileManager instanceof BaseFileManager && ((BaseFileManager) fileManager).autoClose) { 209 try { 210 fileManager.close(); 211 } catch (IOException ignore) { 212 } 213 } 214 compiler = null; 215 context = null; 216 notYetEntered = null; 217 } 218 219 @Override @DefinedBy(Api.COMPILER_TREE) 220 public Iterable<? extends CompilationUnitTree> parse() { 221 return handleExceptions(new Callable<Iterable<? extends CompilationUnitTree>>() { 222 @Override 223 public Iterable<? extends CompilationUnitTree> call() { 224 return parseInternal(); 225 } 226 }, List.<CompilationUnitTree>nil(), List.<CompilationUnitTree>nil()); 227 } 228 229 private Iterable<? extends CompilationUnitTree> parseInternal() { 230 try { 231 prepareCompiler(true); 232 List<JCCompilationUnit> units = compiler.parseFiles(args.getFileObjects()); 233 for (JCCompilationUnit unit: units) { 234 JavaFileObject file = unit.getSourceFile(); 235 if (notYetEntered.containsKey(file)) 236 notYetEntered.put(file, unit); 237 } 238 return units; 239 } 240 finally { 241 parsed = true; 242 if (compiler != null && compiler.log != null) 243 compiler.log.flush(); 244 } 245 } 246 247 private boolean parsed = false; 248 249 /** 250 * Translate all the abstract syntax trees to elements. 251 * 252 * @return a list of elements corresponding to the top level 253 * classes in the abstract syntax trees 254 */ 255 public Iterable<? extends TypeElement> enter() { 256 return enter(null); 257 } 258 259 /** 260 * Translate the given abstract syntax trees to elements. 261 * 262 * @param trees a list of abstract syntax trees. 263 * @return a list of elements corresponding to the top level 264 * classes in the abstract syntax trees 265 */ 266 public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees) 267 { 268 if (trees == null && notYetEntered != null && notYetEntered.isEmpty()) 269 return List.nil(); 270 271 prepareCompiler(true); 272 273 ListBuffer<JCCompilationUnit> roots = null; 274 275 if (trees == null) { 276 // If there are still files which were specified to be compiled 277 // (i.e. in fileObjects) but which have not yet been entered, 278 // then we make sure they have been parsed and add them to the 279 // list to be entered. 280 if (notYetEntered.size() > 0) { 281 if (!parsed) 282 parseInternal(); // TODO would be nice to specify files needed to be parsed 283 for (JavaFileObject file: args.getFileObjects()) { 284 JCCompilationUnit unit = notYetEntered.remove(file); 285 if (unit != null) { 286 if (roots == null) 287 roots = new ListBuffer<>(); 288 roots.append(unit); 289 } 290 } 291 notYetEntered.clear(); 292 } 293 } 294 else { 295 for (CompilationUnitTree cu : trees) { 296 if (cu instanceof JCCompilationUnit) { 297 if (roots == null) 298 roots = new ListBuffer<>(); 299 roots.append((JCCompilationUnit)cu); 300 notYetEntered.remove(cu.getSourceFile()); 301 } 302 else 303 throw new IllegalArgumentException(cu.toString()); 304 } 305 } 306 307 if (roots == null) 308 return List.nil(); 309 310 try { 311 List<JCCompilationUnit> units = compiler.enterTrees(roots.toList()); 312 313 if (notYetEntered.isEmpty()) 314 compiler.processAnnotations(units); 315 316 ListBuffer<TypeElement> elements = new ListBuffer<>(); 317 for (JCCompilationUnit unit : units) { 318 for (JCTree node : unit.defs) { 319 if (node.hasTag(JCTree.Tag.CLASSDEF)) { 320 JCClassDecl cdef = (JCClassDecl) node; 321 if (cdef.sym != null) // maybe null if errors in anno processing 322 elements.append(cdef.sym); 323 } 324 } 325 } 326 return elements.toList(); 327 } 328 finally { 329 compiler.log.flush(); 330 } 331 } 332 333 @Override @DefinedBy(Api.COMPILER_TREE) 334 public Iterable<? extends Element> analyze() { 335 return handleExceptions(new Callable<Iterable<? extends Element>>() { 336 @Override 337 public Iterable<? extends Element> call() { 338 return analyze(null); 339 } 340 }, List.<Element>nil(), List.<Element>nil()); 341 } 342 343 /** 344 * Complete all analysis on the given classes. 345 * This can be used to ensure that all compile time errors are reported. 346 * The classes must have previously been returned from {@link #enter}. 347 * If null is specified, all outstanding classes will be analyzed. 348 * 349 * @param classes a list of class elements 350 * @return the elements that were analyzed 351 */ 352 // This implementation requires that we open up privileges on JavaCompiler. 353 // An alternative implementation would be to move this code to JavaCompiler and 354 // wrap it here 355 public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) { 356 enter(null); // ensure all classes have been entered 357 358 final ListBuffer<Element> results = new ListBuffer<>(); 359 try { 360 if (classes == null) { 361 handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results); 362 } else { 363 Filter f = new Filter() { 364 @Override 365 public void process(Env<AttrContext> env) { 366 handleFlowResults(compiler.flow(compiler.attribute(env)), results); 367 } 368 }; 369 f.run(compiler.todo, classes); 370 } 371 } finally { 372 compiler.log.flush(); 373 } 374 return results; 375 } 376 // where 377 private void handleFlowResults(Queue<Env<AttrContext>> queue, ListBuffer<Element> elems) { 378 for (Env<AttrContext> env: queue) { 379 switch (env.tree.getTag()) { 380 case CLASSDEF: 381 JCClassDecl cdef = (JCClassDecl) env.tree; 382 if (cdef.sym != null) 383 elems.append(cdef.sym); 384 break; 385 case TOPLEVEL: 386 JCCompilationUnit unit = (JCCompilationUnit) env.tree; 387 if (unit.packge != null) 388 elems.append(unit.packge); 389 break; 390 } 391 } 392 genList.addAll(queue); 393 } 394 395 @Override @DefinedBy(Api.COMPILER_TREE) 396 public Iterable<? extends JavaFileObject> generate() { 397 return handleExceptions(new Callable<Iterable<? extends JavaFileObject>>() { 398 @Override 399 public Iterable<? extends JavaFileObject> call() { 400 return generate(null); 401 } 402 }, List.<JavaFileObject>nil(), List.<JavaFileObject>nil()); 403 } 404 405 /** 406 * Generate code corresponding to the given classes. 407 * The classes must have previously been returned from {@link #enter}. 408 * If there are classes outstanding to be analyzed, that will be done before 409 * any classes are generated. 410 * If null is specified, code will be generated for all outstanding classes. 411 * 412 * @param classes a list of class elements 413 * @return the files that were generated 414 */ 415 public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) { 416 final ListBuffer<JavaFileObject> results = new ListBuffer<>(); 417 try { 418 analyze(null); // ensure all classes have been parsed, entered, and analyzed 419 420 if (classes == null) { 421 compiler.generate(compiler.desugar(genList), results); 422 genList.clear(); 423 } 424 else { 425 Filter f = new Filter() { 426 @Override 427 public void process(Env<AttrContext> env) { 428 compiler.generate(compiler.desugar(ListBuffer.of(env)), results); 429 } 430 }; 431 f.run(genList, classes); 432 } 433 if (genList.isEmpty()) { 434 compiler.reportDeferredDiagnostics(); 435 cleanup(); 436 } 437 } 438 finally { 439 if (compiler != null) 440 compiler.log.flush(); 441 } 442 return results; 443 } 444 445 public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) { 446 return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse(); 447 } 448 449 abstract class Filter { 450 void run(Queue<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) { 451 Set<TypeElement> set = new HashSet<>(); 452 for (TypeElement item: classes) 453 set.add(item); 454 455 ListBuffer<Env<AttrContext>> defer = new ListBuffer<>(); 456 while (list.peek() != null) { 457 Env<AttrContext> env = list.remove(); 458 ClassSymbol csym = env.enclClass.sym; 459 if (csym != null && set.contains(csym.outermostClass())) 460 process(env); 461 else 462 defer = defer.append(env); 463 } 464 465 list.addAll(defer); 466 } 467 468 abstract void process(Env<AttrContext> env); 469 } 470 471 /** 472 * For internal use only. This method will be 473 * removed without warning. 474 * @param expr the type expression to be analyzed 475 * @param scope the scope in which to analyze the type expression 476 * @return the type 477 * @throws IllegalArgumentException if the type expression of null or empty 478 */ 479 public Type parseType(String expr, TypeElement scope) { 480 if (expr == null || expr.equals("")) 481 throw new IllegalArgumentException(); 482 compiler = JavaCompiler.instance(context); 483 JavaFileObject prev = compiler.log.useSource(null); 484 ParserFactory parserFactory = ParserFactory.instance(context); 485 Attr attr = Attr.instance(context); 486 try { 487 CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length()); 488 Parser parser = parserFactory.newParser(buf, false, false, false); 489 JCTree tree = parser.parseType(); 490 return attr.attribType(tree, (Symbol.TypeSymbol)scope); 491 } finally { 492 compiler.log.useSource(prev); 493 } 494 } 495 496} 497