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