1 /* 2 * Copyright (c) 1999, 2015, 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.code; 27 28 import java.io.IOException; 29 import java.io.File; 30 import java.util.EnumSet; 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.Set; 34 35 import javax.lang.model.SourceVersion; 36 import javax.tools.JavaFileManager; 37 import javax.tools.JavaFileManager.Location; 38 import javax.tools.JavaFileObject; 39 import javax.tools.StandardJavaFileManager; 40 41 import com.sun.tools.javac.code.Scope.WriteableScope; 42 import com.sun.tools.javac.code.Symbol.ClassSymbol; 43 import com.sun.tools.javac.code.Symbol.Completer; 44 import com.sun.tools.javac.code.Symbol.CompletionFailure; 45 import com.sun.tools.javac.code.Symbol.PackageSymbol; 46 import com.sun.tools.javac.code.Symbol.TypeSymbol; 47 import com.sun.tools.javac.comp.Annotate; 48 import com.sun.tools.javac.comp.Enter; 49 import com.sun.tools.javac.file.JRTIndex; 50 import com.sun.tools.javac.file.JavacFileManager; 51 import com.sun.tools.javac.jvm.ClassReader; 52 import com.sun.tools.javac.jvm.Profile; 53 import com.sun.tools.javac.util.*; 54 55 import static javax.tools.StandardLocation.*; 56 57 import static com.sun.tools.javac.code.Flags.*; 58 import static com.sun.tools.javac.code.Kinds.Kind.*; 59 60 import static com.sun.tools.javac.main.Option.*; 61 import com.sun.tools.javac.util.Dependencies.CompletionCause; 62 63 /** 64 * This class provides operations to locate class definitions 65 * from the source and class files on the paths provided to javac. 66 * 67 * <p><b>This is NOT part of any supported API. 68 * If you write code that depends on this, you do so at your own risk. 69 * This code and its internal interfaces are subject to change or 70 * deletion without notice.</b> 71 */ 72 public class ClassFinder { 73 /** The context key for the class finder. */ 74 protected static final Context.Key<ClassFinder> classFinderKey = new Context.Key<>(); 75 76 ClassReader reader; 77 78 private final Annotate annotate; 79 80 /** Switch: verbose output. 81 */ 82 boolean verbose; 83 84 /** 85 * Switch: cache completion failures unless -XDdev is used 86 */ 87 private boolean cacheCompletionFailure; 88 89 /** 90 * Switch: prefer source files instead of newer when both source 91 * and class are available 92 **/ 93 protected boolean preferSource; 94 95 /** 96 * Switch: Search classpath and sourcepath for classes before the 97 * bootclasspath 98 */ 99 protected boolean userPathsFirst; 100 101 /** The log to use for verbose output 102 */ 103 final Log log; 104 105 /** The symbol table. */ 106 Symtab syms; 107 108 /** The name table. */ 109 final Names names; 110 111 /** Force a completion failure on this name 112 */ 113 final Name completionFailureName; 114 115 /** Access to files 116 */ 117 private final JavaFileManager fileManager; 118 119 /** Dependency tracker 120 */ 121 private final Dependencies dependencies; 122 123 /** Factory for diagnostics 124 */ 125 JCDiagnostic.Factory diagFactory; 126 127 /** Can be reassigned from outside: 128 * the completer to be used for ".java" files. If this remains unassigned 129 * ".java" files will not be loaded. 130 */ 131 public Completer sourceCompleter = Completer.NULL_COMPLETER; 132 133 /** The path name of the class file currently being read. 134 */ 135 protected JavaFileObject currentClassFile = null; 136 137 /** The class or method currently being read. 138 */ 139 protected Symbol currentOwner = null; 140 141 /** 142 * The currently selected profile. 143 */ 144 private final Profile profile; 145 146 /** 147 * Use direct access to the JRTIndex to access the temporary 148 * replacement for the info that used to be in ct.sym. 149 * In time, this will go away and be replaced by the module system. 150 */ 151 private final JRTIndex jrtIndex; 152 153 /** 154 * Completer that delegates to the complete-method of this class. 155 */ 156 private final Completer thisCompleter = new Completer() { 157 @Override 158 public void complete(Symbol sym) throws CompletionFailure { 159 ClassFinder.this.complete(sym); 160 } 161 }; 162 163 public Completer getCompleter() { 164 return thisCompleter; 165 } 166 167 /** Get the ClassFinder instance for this invocation. */ 168 public static ClassFinder instance(Context context) { 169 ClassFinder instance = context.get(classFinderKey); 170 if (instance == null) 171 instance = new ClassFinder(context); 172 return instance; 173 } 174 175 /** Construct a new class reader. */ 176 protected ClassFinder(Context context) { 177 context.put(classFinderKey, this); 178 reader = ClassReader.instance(context); 179 names = Names.instance(context); 180 syms = Symtab.instance(context); 181 fileManager = context.get(JavaFileManager.class); 182 dependencies = Dependencies.instance(context); 183 if (fileManager == null) 184 throw new AssertionError("FileManager initialization error"); 185 diagFactory = JCDiagnostic.Factory.instance(context); 186 187 log = Log.instance(context); 188 annotate = Annotate.instance(context); 189 190 Options options = Options.instance(context); 191 verbose = options.isSet(VERBOSE); 192 cacheCompletionFailure = options.isUnset("dev"); 193 preferSource = "source".equals(options.get("-Xprefer")); 194 userPathsFirst = options.isSet(XXUSERPATHSFIRST); 195 196 completionFailureName = 197 options.isSet("failcomplete") 198 ? names.fromString(options.get("failcomplete")) 199 : null; 200 201 // Temporary, until more info is available from the module system. 202 boolean useCtProps; 203 JavaFileManager fm = context.get(JavaFileManager.class); 204 if (fm instanceof JavacFileManager) { 205 JavacFileManager jfm = (JavacFileManager) fm; 206 useCtProps = jfm.isDefaultBootClassPath() && jfm.isSymbolFileEnabled(); 207 } else if (fm.getClass().getName().equals("com.sun.tools.sjavac.comp.SmartFileManager")) { 208 useCtProps = !options.isSet("ignore.symbol.file"); 209 } else { 210 useCtProps = false; 211 } 212 jrtIndex = useCtProps && JRTIndex.isAvailable() ? JRTIndex.getSharedInstance() : null; 213 214 profile = Profile.instance(context); 215 } 216 217 218 /************************************************************************ 219 * Temporary ct.sym replacement 220 * 221 * The following code is a temporary substitute for the ct.sym mechanism 222 * used in JDK 6 thru JDK 8. 223 * This mechanism will eventually be superseded by the Jigsaw module system. 224 ***********************************************************************/ 225 226 /** 227 * Returns any extra flags for a class symbol. 228 * This information used to be provided using private annotations 229 * in the class file in ct.sym; in time, this information will be 230 * available from the module system. 231 */ 232 long getSupplementaryFlags(ClassSymbol c) { 233 if (jrtIndex == null || !jrtIndex.isInJRT(c.classfile)) { 234 return 0; 235 } 236 237 if (supplementaryFlags == null) { 238 supplementaryFlags = new HashMap<>(); 239 } 240 241 Long flags = supplementaryFlags.get(c.packge()); 242 if (flags == null) { 243 long newFlags = 0; 244 try { 245 JRTIndex.CtSym ctSym = jrtIndex.getCtSym(c.packge().flatName()); 246 Profile minProfile = Profile.DEFAULT; 247 if (ctSym.proprietary) 248 newFlags |= PROPRIETARY; 249 if (ctSym.minProfile != null) 250 minProfile = Profile.lookup(ctSym.minProfile); 251 if (profile != Profile.DEFAULT && minProfile.value > profile.value) { 252 newFlags |= NOT_IN_PROFILE; 253 } 254 } catch (IOException ignore) { 255 } 256 supplementaryFlags.put(c.packge(), flags = newFlags); 257 } 258 return flags; 259 } 260 261 private Map<PackageSymbol, Long> supplementaryFlags; 262 263 /************************************************************************ 264 * Loading Classes 265 ***********************************************************************/ 266 267 /** Completion for classes to be loaded. Before a class is loaded 268 * we make sure its enclosing class (if any) is loaded. 269 */ 270 private void complete(Symbol sym) throws CompletionFailure { 271 if (sym.kind == TYP) { 272 try { 273 ClassSymbol c = (ClassSymbol) sym; 274 dependencies.push(c, CompletionCause.CLASS_READER); 275 annotate.blockAnnotations(); 276 c.members_field = new Scope.ErrorScope(c); // make sure it's always defined 277 completeOwners(c.owner); 278 completeEnclosing(c); 279 fillIn(c); 280 } finally { 281 annotate.unblockAnnotationsNoFlush(); 282 dependencies.pop(); 283 } 284 } else if (sym.kind == PCK) { 285 PackageSymbol p = (PackageSymbol)sym; 286 try { 287 fillIn(p); 288 } catch (IOException ex) { 289 throw new CompletionFailure(sym, ex.getLocalizedMessage()).initCause(ex); 290 } 291 } 292 if (!reader.filling) 293 annotate.flush(); // finish attaching annotations 294 } 295 296 /** complete up through the enclosing package. */ 297 private void completeOwners(Symbol o) { 298 if (o.kind != PCK) completeOwners(o.owner); 299 o.complete(); 300 } 301 302 /** 303 * Tries to complete lexically enclosing classes if c looks like a 304 * nested class. This is similar to completeOwners but handles 305 * the situation when a nested class is accessed directly as it is 306 * possible with the Tree API or javax.lang.model.*. 307 */ 308 private void completeEnclosing(ClassSymbol c) { 309 if (c.owner.kind == PCK) { 310 Symbol owner = c.owner; 311 for (Name name : Convert.enclosingCandidates(Convert.shortName(c.name))) { 312 Symbol encl = owner.members().findFirst(name); 313 if (encl == null) 314 encl = syms.classes.get(TypeSymbol.formFlatName(name, owner)); 315 if (encl != null) 316 encl.complete(); 317 } 318 } 319 } 320 321 /** Fill in definition of class `c' from corresponding class or 322 * source file. 323 */ 324 private void fillIn(ClassSymbol c) { 325 if (completionFailureName == c.fullname) { 326 throw new CompletionFailure(c, "user-selected completion failure by class name"); 327 } 328 currentOwner = c; 329 JavaFileObject classfile = c.classfile; 330 if (classfile != null) { 331 JavaFileObject previousClassFile = currentClassFile; 332 try { 333 if (reader.filling) { 334 Assert.error("Filling " + classfile.toUri() + " during " + previousClassFile); 335 } 336 currentClassFile = classfile; 337 if (verbose) { 338 log.printVerbose("loading", currentClassFile.toString()); 339 } 340 if (classfile.getKind() == JavaFileObject.Kind.CLASS) { 341 reader.readClassFile(c); 342 c.flags_field |= getSupplementaryFlags(c); 343 } else { 344 if (!sourceCompleter.isTerminal()) { 345 sourceCompleter.complete(c); 346 } else { 347 throw new IllegalStateException("Source completer required to read " 348 + classfile.toUri()); 349 } 350 } 351 } finally { 352 currentClassFile = previousClassFile; 353 } 354 } else { 355 throw classFileNotFound(c); 356 } 357 } 358 // where 359 private CompletionFailure classFileNotFound(ClassSymbol c) { 360 JCDiagnostic diag = 361 diagFactory.fragment("class.file.not.found", c.flatname); 362 return newCompletionFailure(c, diag); 363 } 364 /** Static factory for CompletionFailure objects. 365 * In practice, only one can be used at a time, so we share one 366 * to reduce the expense of allocating new exception objects. 367 */ 368 private CompletionFailure newCompletionFailure(TypeSymbol c, 369 JCDiagnostic diag) { 370 if (!cacheCompletionFailure) { 371 // log.warning("proc.messager", 372 // Log.getLocalizedString("class.file.not.found", c.flatname)); 373 // c.debug.printStackTrace(); 374 return new CompletionFailure(c, diag); 375 } else { 376 CompletionFailure result = cachedCompletionFailure; 377 result.sym = c; 378 result.diag = diag; 379 return result; 380 } 381 } 382 private final CompletionFailure cachedCompletionFailure = 383 new CompletionFailure(null, (JCDiagnostic) null); 384 { 385 cachedCompletionFailure.setStackTrace(new StackTraceElement[0]); 386 } 387 388 389 /** Load a toplevel class with given fully qualified name 390 * The class is entered into `classes' only if load was successful. 391 */ 392 public ClassSymbol loadClass(Name flatname) throws CompletionFailure { 393 boolean absent = syms.classes.get(flatname) == null; 394 ClassSymbol c = syms.enterClass(flatname); 395 if (c.members_field == null) { 396 try { 397 c.complete(); 398 } catch (CompletionFailure ex) { 399 if (absent) syms.classes.remove(flatname); 400 throw ex; 401 } 402 } 403 return c; 404 } 405 406 /************************************************************************ 407 * Loading Packages 408 ***********************************************************************/ 409 410 /** Include class corresponding to given class file in package, 411 * unless (1) we already have one the same kind (.class or .java), or 412 * (2) we have one of the other kind, and the given class file 413 * is older. 414 */ 415 protected void includeClassFile(PackageSymbol p, JavaFileObject file) { 416 if ((p.flags_field & EXISTS) == 0) 417 for (Symbol q = p; q != null && q.kind == PCK; q = q.owner) 418 q.flags_field |= EXISTS; 419 JavaFileObject.Kind kind = file.getKind(); 420 int seen; 421 if (kind == JavaFileObject.Kind.CLASS) 422 seen = CLASS_SEEN; 423 else 424 seen = SOURCE_SEEN; 425 String binaryName = fileManager.inferBinaryName(currentLoc, file); 426 int lastDot = binaryName.lastIndexOf("."); 427 Name classname = names.fromString(binaryName.substring(lastDot + 1)); 428 boolean isPkgInfo = classname == names.package_info; 429 ClassSymbol c = isPkgInfo 430 ? p.package_info 431 : (ClassSymbol) p.members_field.findFirst(classname); 432 if (c == null) { 433 c = syms.enterClass(classname, p); 434 if (c.classfile == null) // only update the file if's it's newly created 435 c.classfile = file; 436 if (isPkgInfo) { 437 p.package_info = c; 438 } else { 439 if (c.owner == p) // it might be an inner class 440 p.members_field.enter(c); 441 } 442 } else if (!preferCurrent && c.classfile != null && (c.flags_field & seen) == 0) { 443 // if c.classfile == null, we are currently compiling this class 444 // and no further action is necessary. 445 // if (c.flags_field & seen) != 0, we have already encountered 446 // a file of the same kind; again no further action is necessary. 447 if ((c.flags_field & (CLASS_SEEN | SOURCE_SEEN)) != 0) 448 c.classfile = preferredFileObject(file, c.classfile); 449 } 450 c.flags_field |= seen; 451 } 452 453 /** Implement policy to choose to derive information from a source 454 * file or a class file when both are present. May be overridden 455 * by subclasses. 456 */ 457 protected JavaFileObject preferredFileObject(JavaFileObject a, 458 JavaFileObject b) { 459 460 if (preferSource) 461 return (a.getKind() == JavaFileObject.Kind.SOURCE) ? a : b; 462 else { 463 long adate = a.getLastModified(); 464 long bdate = b.getLastModified(); 465 // 6449326: policy for bad lastModifiedTime in ClassReader 466 //assert adate >= 0 && bdate >= 0; 467 return (adate > bdate) ? a : b; 468 } 469 } 470 471 /** 472 * specifies types of files to be read when filling in a package symbol 473 */ 474 protected EnumSet<JavaFileObject.Kind> getPackageFileKinds() { 475 return EnumSet.of(JavaFileObject.Kind.CLASS, JavaFileObject.Kind.SOURCE); 476 } 477 478 /** 479 * this is used to support javadoc 480 */ 481 protected void extraFileActions(PackageSymbol pack, JavaFileObject fe) { 482 } 483 484 protected Location currentLoc; // FIXME 485 486 private boolean verbosePath = true; 487 488 // Set to true when the currently selected file should be kept 489 private boolean preferCurrent; 490 491 /** Load directory of package into members scope. 492 */ 493 private void fillIn(PackageSymbol p) throws IOException { 494 if (p.members_field == null) 495 p.members_field = WriteableScope.create(p); 496 497 preferCurrent = false; 498 if (userPathsFirst) { 499 scanUserPaths(p); 500 preferCurrent = true; 501 scanPlatformPath(p); 502 } else { 503 scanPlatformPath(p); 504 scanUserPaths(p); 505 } 506 verbosePath = false; 507 } 508 509 /** 510 * Scans class path and source path for files in given package. 511 */ 512 private void scanUserPaths(PackageSymbol p) throws IOException { 513 Set<JavaFileObject.Kind> kinds = getPackageFileKinds(); 514 515 Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds); 516 classKinds.remove(JavaFileObject.Kind.SOURCE); 517 boolean wantClassFiles = !classKinds.isEmpty(); 518 519 Set<JavaFileObject.Kind> sourceKinds = EnumSet.copyOf(kinds); 520 sourceKinds.remove(JavaFileObject.Kind.CLASS); 521 boolean wantSourceFiles = !sourceKinds.isEmpty(); 522 523 boolean haveSourcePath = fileManager.hasLocation(SOURCE_PATH); 524 525 if (verbose && verbosePath) { 526 if (fileManager instanceof StandardJavaFileManager) { 527 StandardJavaFileManager fm = (StandardJavaFileManager)fileManager; 528 if (haveSourcePath && wantSourceFiles) { 529 List<File> path = List.nil(); 530 for (File file : fm.getLocation(SOURCE_PATH)) { 531 path = path.prepend(file); 532 } 533 log.printVerbose("sourcepath", path.reverse().toString()); 534 } else if (wantSourceFiles) { 535 List<File> path = List.nil(); 536 for (File file : fm.getLocation(CLASS_PATH)) { 537 path = path.prepend(file); 538 } 539 log.printVerbose("sourcepath", path.reverse().toString()); 540 } 541 if (wantClassFiles) { 542 List<File> path = List.nil(); 543 for (File file : fm.getLocation(PLATFORM_CLASS_PATH)) { 544 path = path.prepend(file); 545 } 546 for (File file : fm.getLocation(CLASS_PATH)) { 547 path = path.prepend(file); 548 } 549 log.printVerbose("classpath", path.reverse().toString()); 550 } 551 } 552 } 553 554 String packageName = p.fullname.toString(); 555 if (wantSourceFiles && !haveSourcePath) { 556 fillIn(p, CLASS_PATH, 557 fileManager.list(CLASS_PATH, 558 packageName, 559 kinds, 560 false)); 561 } else { 562 if (wantClassFiles) 563 fillIn(p, CLASS_PATH, 564 fileManager.list(CLASS_PATH, 565 packageName, 566 classKinds, 567 false)); 568 if (wantSourceFiles) 569 fillIn(p, SOURCE_PATH, 570 fileManager.list(SOURCE_PATH, 571 packageName, 572 sourceKinds, 573 false)); 574 } 575 } 576 577 /** 578 * Scans platform class path for files in given package. 579 */ 580 private void scanPlatformPath(PackageSymbol p) throws IOException { 581 fillIn(p, PLATFORM_CLASS_PATH, 582 fileManager.list(PLATFORM_CLASS_PATH, 583 p.fullname.toString(), 584 EnumSet.of(JavaFileObject.Kind.CLASS), 585 false)); 586 } 587 // where 588 private void fillIn(PackageSymbol p, 589 Location location, 590 Iterable<JavaFileObject> files) 591 { 592 currentLoc = location; 593 for (JavaFileObject fo : files) { 594 switch (fo.getKind()) { 595 case CLASS: 596 case SOURCE: { 597 // TODO pass binaryName to includeClassFile 598 String binaryName = fileManager.inferBinaryName(currentLoc, fo); 599 String simpleName = binaryName.substring(binaryName.lastIndexOf(".") + 1); 600 if (SourceVersion.isIdentifier(simpleName) || 601 simpleName.equals("package-info")) 602 includeClassFile(p, fo); 603 break; 604 } 605 default: 606 extraFileActions(p, fo); 607 } 608 } 609 } 610 611 /** 612 * Used for bad class definition files, such as bad .class files or 613 * for .java files with unexpected package or class names. 614 */ 615 public static class BadClassFile extends CompletionFailure { 616 private static final long serialVersionUID = 0; 617 618 public BadClassFile(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag, 619 JCDiagnostic.Factory diagFactory) { 620 super(sym, createBadClassFileDiagnostic(file, diag, diagFactory)); 621 } 622 // where 623 private static JCDiagnostic createBadClassFileDiagnostic( 624 JavaFileObject file, JCDiagnostic diag, JCDiagnostic.Factory diagFactory) { 625 String key = (file.getKind() == JavaFileObject.Kind.SOURCE 626 ? "bad.source.file.header" : "bad.class.file.header"); 627 return diagFactory.fragment(key, file, diag); 628 } 629 } 630 }