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