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 Integer 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 == 0; 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 ListBuffer<File>(); 162 List<File> filenames = compilerMain.processArgs(CommandLine.parse(args)); 163 if (!filenames.isEmpty()) 164 throw new IllegalArgumentException("Malformed arguments " + filenames.toString(" ")); 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 private void initContext() { 181 context.put(JavacTaskImpl.class, this); 182 if (context.get(TaskListener.class) != null) 183 context.put(TaskListener.class, (TaskListener)null); 184 if (taskListener != null) 185 context.put(TaskListener.class, ccw.wrap(taskListener)); 186 //initialize compiler's default locale 187 context.put(Locale.class, locale); 188 } 189 190 void cleanup() { 191 if (compiler != null) 192 compiler.close(); 193 compiler = null; 194 compilerMain = null; 195 args = null; 196 context = null; 197 fileObjects = null; 198 notYetEntered = null; 199 } 200 201 /** 202 * Construct a JavaFileObject from the given file. 203 * 204 * <p><b>TODO: this method is useless here</b></p> 205 * 206 * @param file a file 207 * @return a JavaFileObject from the standard file manager. 208 */ 209 public JavaFileObject asJavaFileObject(File file) { 210 JavacFileManager fm = (JavacFileManager)context.get(JavaFileManager.class); 211 return fm.getRegularFile(file); 212 } 213 214 public void setTaskListener(TaskListener taskListener) { 215 this.taskListener = taskListener; 216 } 217 218 /** 219 * Parse the specified files returning a list of abstract syntax trees. 220 * 221 * @throws java.io.IOException TODO 222 * @return a list of abstract syntax trees 223 */ 224 public Iterable<? extends CompilationUnitTree> parse() throws IOException { 225 try { 226 prepareCompiler(); 227 List<JCCompilationUnit> units = compiler.parseFiles(fileObjects); 228 for (JCCompilationUnit unit: units) { 229 JavaFileObject file = unit.getSourceFile(); 230 if (notYetEntered.containsKey(file)) 231 notYetEntered.put(file, unit); 232 } 233 return units; 234 } 235 finally { 236 parsed = true; 237 if (compiler != null && compiler.log != null) 238 compiler.log.flush(); 239 } 240 } 241 242 private boolean parsed = false; 243 244 /** 245 * Translate all the abstract syntax trees to elements. 246 * 247 * @throws IOException TODO 248 * @return a list of elements corresponding to the top level 249 * classes in the abstract syntax trees 250 */ 251 public Iterable<? extends TypeElement> enter() throws IOException { 252 return enter(null); 253 } 254 255 /** 256 * Translate the given abstract syntax trees to elements. 257 * 258 * @param trees a list of abstract syntax trees. 259 * @throws java.io.IOException TODO 260 * @return a list of elements corresponding to the top level 261 * classes in the abstract syntax trees 262 */ 263 public Iterable<? extends TypeElement> enter(Iterable<? extends CompilationUnitTree> trees) 264 throws IOException 265 { 266 prepareCompiler(); 267 268 ListBuffer<JCCompilationUnit> roots = null; 269 270 if (trees == null) { 271 // If there are still files which were specified to be compiled 272 // (i.e. in fileObjects) but which have not yet been entered, 273 // then we make sure they have been parsed and add them to the 274 // list to be entered. 275 if (notYetEntered.size() > 0) { 276 if (!parsed) 277 parse(); // TODO would be nice to specify files needed to be parsed 278 for (JavaFileObject file: fileObjects) { 279 JCCompilationUnit unit = notYetEntered.remove(file); 280 if (unit != null) { 281 if (roots == null) 282 roots = new ListBuffer<JCCompilationUnit>(); 283 roots.append(unit); 284 } 285 } 286 notYetEntered.clear(); 287 } 288 } 289 else { 290 for (CompilationUnitTree cu : trees) { 291 if (cu instanceof JCCompilationUnit) { 292 if (roots == null) 293 roots = new ListBuffer<JCCompilationUnit>(); 294 roots.append((JCCompilationUnit)cu); 295 notYetEntered.remove(cu.getSourceFile()); 296 } 297 else 298 throw new IllegalArgumentException(cu.toString()); 299 } 300 } 301 302 if (roots == null) 303 return List.nil(); 304 305 try { 306 List<JCCompilationUnit> units = compiler.enterTrees(roots.toList()); 307 308 if (notYetEntered.isEmpty()) 309 compiler = compiler.processAnnotations(units); 310 311 ListBuffer<TypeElement> elements = new ListBuffer<TypeElement>(); 312 for (JCCompilationUnit unit : units) { 313 for (JCTree node : unit.defs) { 314 if (node.getTag() == JCTree.CLASSDEF) { 315 JCClassDecl cdef = (JCClassDecl) node; 316 if (cdef.sym != null) // maybe null if errors in anno processing 317 elements.append(cdef.sym); 318 } 319 } 320 } 321 return elements.toList(); 322 } 323 finally { 324 compiler.log.flush(); 325 } 326 } 327 328 /** 329 * Complete all analysis. 330 * @throws IOException TODO 331 */ 332 @Override 333 public Iterable<? extends Element> analyze() throws IOException { 334 return analyze(null); 335 } 336 337 /** 338 * Complete all analysis on the given classes. 339 * This can be used to ensure that all compile time errors are reported. 340 * The classes must have previously been returned from {@link #enter}. 341 * If null is specified, all outstanding classes will be analyzed. 342 * 343 * @param classes a list of class elements 344 */ 345 // This implementation requires that we open up privileges on JavaCompiler. 346 // An alternative implementation would be to move this code to JavaCompiler and 347 // wrap it here 348 public Iterable<? extends Element> analyze(Iterable<? extends TypeElement> classes) throws IOException { 349 enter(null); // ensure all classes have been entered 350 351 final ListBuffer<Element> results = new ListBuffer<Element>(); 352 try { 353 if (classes == null) { 354 handleFlowResults(compiler.flow(compiler.attribute(compiler.todo)), results); 355 } else { 356 Filter f = new Filter() { 357 public void process(Env<AttrContext> env) { 358 handleFlowResults(compiler.flow(compiler.attribute(env)), results); 359 } 360 }; 361 f.run(compiler.todo, classes); 362 } 363 } finally { 364 compiler.log.flush(); 365 } 366 return results; 367 } 368 // where 369 private void handleFlowResults(Queue<Env<AttrContext>> queue, ListBuffer<Element> elems) { 370 for (Env<AttrContext> env: queue) { 371 switch (env.tree.getTag()) { 372 case JCTree.CLASSDEF: 373 JCClassDecl cdef = (JCClassDecl) env.tree; 374 if (cdef.sym != null) 375 elems.append(cdef.sym); 376 break; 377 case JCTree.TOPLEVEL: 378 JCCompilationUnit unit = (JCCompilationUnit) env.tree; 379 if (unit.packge != null) 380 elems.append(unit.packge); 381 break; 382 } 383 } 384 genList.addAll(queue); 385 } 386 387 388 /** 389 * Generate code. 390 * @throws IOException TODO 391 */ 392 @Override 393 public Iterable<? extends JavaFileObject> generate() throws IOException { 394 return generate(null); 395 } 396 397 /** 398 * Generate code corresponding to the given classes. 399 * The classes must have previously been returned from {@link #enter}. 400 * If there are classes outstanding to be analyzed, that will be done before 401 * any classes are generated. 402 * If null is specified, code will be generated for all outstanding classes. 403 * 404 * @param classes a list of class elements 405 */ 406 public Iterable<? extends JavaFileObject> generate(Iterable<? extends TypeElement> classes) throws IOException { 407 final ListBuffer<JavaFileObject> results = new ListBuffer<JavaFileObject>(); 408 try { 409 analyze(null); // ensure all classes have been parsed, entered, and analyzed 410 411 if (classes == null) { 412 compiler.generate(compiler.desugar(genList), results); 413 genList.clear(); 414 } 415 else { 416 Filter f = new Filter() { 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 TypeMirror getTypeMirror(Iterable<? extends Tree> path) { 436 // TODO: Should complete attribution if necessary 437 Tree last = null; 438 for (Tree node : path) 439 last = node; 440 return ((JCTree)last).type; 441 } 442 443 public JavacElements getElements() { 444 if (context == null) 445 throw new IllegalStateException(); 446 return JavacElements.instance(context); 447 } 448 449 public JavacTypes getTypes() { 450 if (context == null) 451 throw new IllegalStateException(); 452 return JavacTypes.instance(context); 453 } 454 455 public Iterable<? extends Tree> pathFor(CompilationUnitTree unit, Tree node) { 456 return TreeInfo.pathFor((JCTree) node, (JCTree.JCCompilationUnit) unit).reverse(); 457 } 458 459 abstract class Filter { 460 void run(Queue<Env<AttrContext>> list, Iterable<? extends TypeElement> classes) { 461 Set<TypeElement> set = new HashSet<TypeElement>(); 462 for (TypeElement item: classes) 463 set.add(item); 464 465 ListBuffer<Env<AttrContext>> defer = ListBuffer.<Env<AttrContext>>lb(); 466 while (list.peek() != null) { 467 Env<AttrContext> env = list.remove(); 468 ClassSymbol csym = env.enclClass.sym; 469 if (csym != null && set.contains(csym.outermostClass())) 470 process(env); 471 else 472 defer = defer.append(env); 473 } 474 475 list.addAll(defer); 476 } 477 478 abstract void process(Env<AttrContext> env); 479 } 480 481 /** 482 * For internal use only. This method will be 483 * removed without warning. 484 */ 485 public Context getContext() { 486 return context; 487 } 488 489 /** 490 * For internal use only. This method will be 491 * removed without warning. 492 */ 493 public void updateContext(Context newContext) { 494 context = newContext; 495 } 496 497 /** 498 * For internal use only. This method will be 499 * removed without warning. 500 */ 501 public Type parseType(String expr, TypeElement scope) { 502 if (expr == null || expr.equals("")) 503 throw new IllegalArgumentException(); 504 compiler = JavaCompiler.instance(context); 505 JavaFileObject prev = compiler.log.useSource(null); 506 ParserFactory parserFactory = ParserFactory.instance(context); 507 Attr attr = Attr.instance(context); 508 try { 509 CharBuffer buf = CharBuffer.wrap((expr+"\u0000").toCharArray(), 0, expr.length()); 510 Parser parser = parserFactory.newParser(buf, false, false, false); 511 JCTree tree = parser.parseType(); 512 return attr.attribType(tree, (Symbol.TypeSymbol)scope); 513 } finally { 514 compiler.log.useSource(prev); 515 } 516 } 517 518 }