1 /* 2 * Copyright (c) 2005, 2011, 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 26 package com.sun.tools.javac.api; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.nio.CharBuffer; 31 import java.util.*; 32 import java.util.concurrent.atomic.AtomicBoolean; 33 34 import javax.annotation.processing.Processor; 35 import javax.lang.model.element.Element; 36 import javax.lang.model.element.TypeElement; 37 import javax.lang.model.type.TypeMirror; 38 import javax.tools.*; 39 40 import com.sun.source.tree.*; 41 import com.sun.source.util.*; 42 import com.sun.tools.javac.code.*; 43 import com.sun.tools.javac.code.Symbol.*; 44 import com.sun.tools.javac.comp.*; 45 import com.sun.tools.javac.file.JavacFileManager; 46 import com.sun.tools.javac.main.*; 47 import com.sun.tools.javac.model.*; 48 import com.sun.tools.javac.parser.Parser; 49 import com.sun.tools.javac.parser.ParserFactory; 50 import com.sun.tools.javac.tree.*; 51 import com.sun.tools.javac.tree.JCTree.*; 52 import com.sun.tools.javac.util.*; 53 import com.sun.tools.javac.util.List; 54 import com.sun.tools.javac.main.JavaCompiler; 55 56 /** 57 * Provides access to functionality specific to the JDK Java Compiler, javac. 58 * 59 * <p><b>This is NOT part of any supported API. 60 * If you write code that depends on this, you do so at your own 61 * risk. This code and its internal interfaces are subject to change 62 * or deletion without notice.</b></p> 63 * 64 * @author Peter von der Ahé 65 * @author Jonathan Gibbons 66 */ 67 public class JavacTaskImpl extends JavacTask { 68 private ClientCodeWrapper ccw; 69 private Main compilerMain; 70 private JavaCompiler compiler; 71 private Locale locale; 72 private String[] args; 73 private Context context; 74 private List<JavaFileObject> fileObjects; 75 private Map<JavaFileObject, JCCompilationUnit> notYetEntered; 76 private ListBuffer<Env<AttrContext>> genList; 77 private TaskListener taskListener; 78 private AtomicBoolean used = new AtomicBoolean(); 79 private Iterable<? extends Processor> processors; 80 81 private Main.Result result = null; 82 83 JavacTaskImpl(Main compilerMain, 84 String[] args, 85 Context context, 86 List<JavaFileObject> fileObjects) { 87 this.ccw = ClientCodeWrapper.instance(context); 88 this.compilerMain = compilerMain; 89 this.args = args; 90 this.context = context; 91 this.fileObjects = fileObjects; 92 setLocale(Locale.getDefault()); 93 // null checks 94 compilerMain.getClass(); 95 args.getClass(); 96 fileObjects.getClass(); 97 } 98 99 JavacTaskImpl(Main compilerMain, 100 Iterable<String> flags, 101 Context context, 102 Iterable<String> classes, 103 Iterable<? extends JavaFileObject> fileObjects) { 104 this(compilerMain, toArray(flags, classes), context, toList(fileObjects)); 105 } 106 107 static private String[] toArray(Iterable<String> flags, Iterable<String> classes) { 108 ListBuffer<String> result = new ListBuffer<String>(); 109 if (flags != null) 110 for (String flag : flags) 111 result.append(flag); 112 if (classes != null) 113 for (String cls : classes) 114 result.append(cls); 115 return result.toArray(new String[result.length()]); 116 } 117 118 static private List<JavaFileObject> toList(Iterable<? extends JavaFileObject> fileObjects) { 119 if (fileObjects == null) 120 return List.nil(); 121 ListBuffer<JavaFileObject> result = new ListBuffer<JavaFileObject>(); 122 for (JavaFileObject fo : fileObjects) 123 result.append(fo); 124 return result.toList(); 125 } 126 127 public Boolean call() { 128 if (!used.getAndSet(true)) { 129 initContext(); 130 notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>(); 131 compilerMain.setAPIMode(true); 132 result = compilerMain.compile(args, context, fileObjects, processors); 133 cleanup(); 134 return result.isOK(); 135 } else { 136 throw new IllegalStateException("multiple calls to method 'call'"); 137 } 138 } 139 140 public void setProcessors(Iterable<? extends Processor> processors) { 141 processors.getClass(); // null check 142 // not mt-safe 143 if (used.get()) 144 throw new IllegalStateException(); 145 this.processors = processors; 146 } 147 148 public void setLocale(Locale locale) { 149 if (used.get()) 150 throw new IllegalStateException(); 151 this.locale = locale; 152 } 153 154 private void prepareCompiler() throws IOException { 155 if (used.getAndSet(true)) { 156 if (compiler == null) 157 throw new IllegalStateException(); 158 } else { 159 initContext(); 160 compilerMain.setOptions(Options.instance(context)); 161 compilerMain.filenames = new LinkedHashSet<File>(); 162 Collection<File> filenames = compilerMain.processArgs(CommandLine.parse(args)); 163 if (!filenames.isEmpty()) 164 throw new IllegalArgumentException("Malformed arguments " + toString(filenames, " ")); 165 compiler = JavaCompiler.instance(context); 166 compiler.keepComments = true; 167 compiler.genEndPos = true; 168 // NOTE: this value will be updated after annotation processing 169 compiler.initProcessAnnotations(processors); 170 notYetEntered = new HashMap<JavaFileObject, JCCompilationUnit>(); 171 for (JavaFileObject file: fileObjects) 172 notYetEntered.put(file, null); 173 genList = new ListBuffer<Env<AttrContext>>(); 174 // endContext will be called when all classes have been generated 175 // TODO: should handle the case after each phase if errors have occurred 176 args = null; 177 } 178 } 179 180 <T> String toString(Iterable<T> items, String sep) { 181 String currSep = ""; 182 StringBuilder sb = new StringBuilder(); 183 for (T item: items) { 184 sb.append(currSep); 185 sb.append(item.toString()); 186 currSep = sep; 187 } 188 return sb.toString(); 189 } 190 191 private void initContext() { 192 context.put(JavacTaskImpl.class, this); 193 if (context.get(TaskListener.class) != null) 194 context.put(TaskListener.class, (TaskListener)null); 195 if (taskListener != null) 196 context.put(TaskListener.class, ccw.wrap(taskListener)); 197 //initialize compiler's default locale 198 context.put(Locale.class, locale); 199 } 200 201 void cleanup() { 202 if (compiler != null) 203 compiler.close(); 204 compiler = null; 205 compilerMain = null; 206 args = null; 207 context = null; 208 fileObjects = null; 209 notYetEntered = null; 210 } 211 212 /** 213 * Construct a JavaFileObject from the given file. 214 * 215 * <p><b>TODO: this method is useless here</b></p> 216 * 217 * @param file a file 218 * @return a JavaFileObject from the standard file manager. 219 */ 220 public JavaFileObject asJavaFileObject(File file) { 221 JavacFileManager fm = (JavacFileManager)context.get(JavaFileManager.class); 222 return fm.getRegularFile(file); 223 } 224 225 public void setTaskListener(TaskListener taskListener) { 226 this.taskListener = taskListener; 227 } 228 229 /** 230 * Parse the specified files returning a list of abstract syntax trees. 231 * 232 * @throws java.io.IOException TODO 233 * @return a list of abstract syntax trees 234 */ 235 public Iterable<? extends CompilationUnitTree> parse() throws IOException { 236 try { 237 prepareCompiler(); 238 List<JCCompilationUnit> units = compiler.parseFiles(fileObjects); 239 for (JCCompilationUnit unit: units) { 240 JavaFileObject file = unit.getSourceFile(); 241 if (notYetEntered.containsKey(file)) 242 notYetEntered.put(file, unit); 243 } 244 return units; 245 } 246 finally { 247 parsed = true; 248 if (compiler != null && compiler.log != null) 249 compiler.log.flush(); 250 } 251 } 252 253 private boolean parsed = false; 254 255 /** 256 * Translate all the abstract syntax trees to elements. 257 * 258 * @throws IOException TODO 259 * @return a list of elements corresponding to the top level 260 * classes in the abstract syntax trees 261 */ 262 public Iterable<? extends TypeElement> enter() throws IOException { 263 return enter(null); 264 } 265 266 /** 267 * Translate the given abstract syntax trees to elements. 268 * 269 * @param trees a list of abstract syntax trees. 270 * @throws java.io.IOException TODO 271 * @return a list of elements corresponding to the top level 272 * classes in the abstract syntax trees 273 */ 274 public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees) 275 throws IOException 276 { 277 if (trees == null && notYetEntered != null && notYetEntered.isEmpty()) 278 return List.nil(); 279 280 prepareCompiler(); 281 282 ListBuffer<JCCompilationUnit> roots = null; 283 284 if (trees == null) { 285 // If there are still files which were specified to be compiled 286 // (i.e. in fileObjects) but which have not yet been entered, 287 // then we make sure they have been parsed and add them to the 288 // list to be entered. 289 if (notYetEntered.size() > 0) { 290 if (!parsed) 291 parse(); // TODO would be nice to specify files needed to be parsed 292 for (JavaFileObject file: fileObjects) { 293 JCCompilationUnit unit = notYetEntered.remove(file); 294 if (unit != null) { 295 if (roots == null) 296 roots = new ListBuffer<JCCompilationUnit>(); 297 roots.append(unit); 298 } 299 } 300 notYetEntered.clear(); 301 } 302 } 303 else { 304 for (CompilationUnitTree cu : trees) { 305 if (cu instanceof JCCompilationUnit) { 306 if (roots == null) 307 roots = new ListBuffer<JCCompilationUnit>(); 308 roots.append((JCCompilationUnit)cu); 309 notYetEntered.remove(cu.getSourceFile()); 310 } 311 else 312 throw new IllegalArgumentException(cu.toString()); 313 } 314 } 315 316 if (roots == null) 317 return List.nil(); 318 319 try { 320 List<JCCompilationUnit> units = compiler.enterTrees(roots.toList()); 321 322 if (notYetEntered.isEmpty()) 323 compiler = compiler.processAnnotations(units); 324 325 ListBuffer<TypeElement> elements = new ListBuffer<TypeElement>(); 326 for (JCCompilationUnit unit : units) { 327 for (JCTree node : unit.defs) { 328 if (node.getTag() == JCTree.CLASSDEF) { 329 JCClassDecl cdef = (JCClassDecl) node; 330 if (cdef.sym != null) // maybe null if errors in anno processing 331 elements.append(cdef.sym); 332 } 333 } 334 } 335 return elements.toList(); 336 } 337 finally { 338 compiler.log.flush(); 339 } 340 } 341 342 /** 343 * Complete all analysis. 344 * @throws IOException TODO 345 */ 346 @Override 347 public Iterable<? extends Element> analyze() throws IOException { 348 return analyze(null); 349 } 350 351 /** 352 * Complete all analysis on the given classes. 353 * This can be used to ensure that all compile time errors are reported. 354 * The classes must have previously been returned from {@link #enter}. 355 * If null is specified, all outstanding classes will be analyzed. 356 * 357 * @param classes a list of class elements 358 */ 359 // This implementation requires that we open up privileges on JavaCompiler. 360 // An alternative implementation would be to move this code to JavaCompiler and 361 // wrap it here 362 public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) throws IOException { 363 enter(null); // ensure all classes have been entered 364 365 final ListBuffer<Element> results = new ListBuffer<Element>(); 366 try { 367 if (classes == null) { 368 handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results); 369 } else { 370 Filter f = new Filter() { 371 public void process(Env<AttrContext> env) { 372 handleFlowResults(compiler.flow(compiler.attribute(env)), results); 373 } 374 }; 375 f.run(compiler.todo, classes); 376 } 377 } finally { 378 compiler.log.flush(); 379 } 380 return results; 381 } 382 // where 383 private void handleFlowResults(Queue<Env<AttrContext>> queue, ListBuffer<Element> elems) { 384 for (Env<AttrContext> env: queue) { 385 switch (env.tree.getTag()) { 386 case JCTree.CLASSDEF: 387 JCClassDecl cdef = (JCClassDecl) env.tree; 388 if (cdef.sym != null) 389 elems.append(cdef.sym); 390 break; 391 case JCTree.TOPLEVEL: 392 JCCompilationUnit unit = (JCCompilationUnit) env.tree; 393 if (unit.packge != null) 394 elems.append(unit.packge); 395 break; 396 } 397 } 398 genList.addAll(queue); 399 } 400 401 402 /** 403 * Generate code. 404 * @throws IOException TODO 405 */ 406 @Override 407 public Iterable<? extends JavaFileObject> generate() throws IOException { 408 return generate(null); 409 } 410 411 /** 412 * Generate code corresponding to the given classes. 413 * The classes must have previously been returned from {@link #enter}. 414 * If there are classes outstanding to be analyzed, that will be done before 415 * any classes are generated. 416 * If null is specified, code will be generated for all outstanding classes. 417 * 418 * @param classes a list of class elements 419 */ 420 public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) throws IOException { 421 final ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>(); 422 try { 423 analyze(null); // ensure all classes have been parsed, entered, and analyzed 424 425 if (classes == null) { 426 compiler.generate(compiler.desugar(genList), results); 427 genList.clear(); 428 } 429 else { 430 Filter f = new Filter() { 431 public void process(Env<AttrContext> env) { 432 compiler.generate(compiler.desugar(ListBuffer.of(env)), results); 433 } 434 }; 435 f.run(genList, classes); 436 } 437 if (genList.isEmpty()) { 438 compiler.reportDeferredDiagnostics(); 439 cleanup(); 440 } 441 } 442 finally { 443 if (compiler != null) 444 compiler.log.flush(); 445 } 446 return results; 447 } 448 449 public TypeMirror getTypeMirror(Iterable<? extends Tree> path) { 450 // TODO: Should complete attribution if necessary 451 Tree last = null; 452 for (Tree node : path) 453 last = node; 454 return ((JCTree)last).type; 455 } 456 457 public JavacElements getElements() { 458 if (context == null) 459 throw new IllegalStateException(); 460 return JavacElements.instance(context); 461 } 462 463 public JavacTypes getTypes() { 464 if (context == null) 465 throw new IllegalStateException(); 466 return JavacTypes.instance(context); 467 } 468 469 public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) { 470 return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse(); 471 } 472 473 abstract class Filter { 474 void run(Queue<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) { 475 Set<TypeElement> set = new HashSet<TypeElement>(); 476 for (TypeElement item: classes) 477 set.add(item); 478 479 ListBuffer<Env<AttrContext>> defer = ListBuffer.<Env<AttrContext>>lb(); 480 while (list.peek() != null) { 481 Env<AttrContext> env = list.remove(); 482 ClassSymbol csym = env.enclClass.sym; 483 if (csym != null && set.contains(csym.outermostClass())) 484 process(env); 485 else 486 defer = defer.append(env); 487 } 488 489 list.addAll(defer); 490 } 491 492 abstract void process(Env<AttrContext> env); 493 } 494 495 /** 496 * For internal use only. This method will be 497 * removed without warning. 498 */ 499 public Context getContext() { 500 return context; 501 } 502 503 /** 504 * For internal use only. This method will be 505 * removed without warning. 506 */ 507 public void updateContext(Context newContext) { 508 context = newContext; 509 } 510 511 /** 512 * For internal use only. This method will be 513 * removed without warning. 514 */ 515 public Type parseType(String expr, TypeElement scope) { 516 if (expr == null || expr.equals("")) 517 throw new IllegalArgumentException(); 518 compiler = JavaCompiler.instance(context); 519 JavaFileObject prev = compiler.log.useSource(null); 520 ParserFactory parserFactory = ParserFactory.instance(context); 521 Attr attr = Attr.instance(context); 522 try { 523 CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length()); 524 Parser parser = parserFactory.newParser(buf, false, false, false); 525 JCTree tree = parser.parseType(); 526 return attr.attribType(tree, (Symbol.TypeSymbol)scope); 527 } finally { 528 compiler.log.useSource(prev); 529 } 530 } 531 532 }